home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) '''The default Script for presenting information to the user using both speech and Braille. This is based primarily on the de-facto standard implementation of the AT-SPI, which is the GAIL support for GTK.''' __id__ = '$Id: default.py 4675 2009-04-11 22:43:39Z wwalker $' __version__ = '$Revision: 4675 $' __date__ = '$Date: 2009-04-11 18:43:39 -0400 (Sat, 11 Apr 2009) $' __copyright__ = 'Copyright (c) 2005-2008 Sun Microsystems Inc.' __license__ = 'LGPL' import locale import math import sys import time import pyatspi import braille import chnames import debug import find import flat_review import input_event import keybindings import mag import outline import orca import orca_prefs import orca_state import phonnames import pronunciation_dict import punctuation_settings import rolenames import script import settings import speech import speechserver import mouse_review import text_attribute_names from orca_i18n import _ from orca_i18n import ngettext from orca_i18n import C_ class Script(script.Script): EMBEDDED_OBJECT_CHARACTER = u'Ôøº' NO_BREAK_SPACE_CHARACTER = u'¬†' def __init__(self, app): '''Creates a new script for the given application. Arguments: - app: the application to create a script for. ''' script.Script.__init__(self, app) self.flatReviewContext = None self.windowActivateTime = None self.lastReviewCurrentEvent = None self.exitLearnModeKeyBinding = None self.targetCursorCell = None self.justEnteredFlatReviewMode = False self.digits = '0123456789' self.whitespace = ' \t\n\r\x0b\x0c' self.lastWhereAmIEvent = None self.lastSayAllEvent = None self._unicodeCurrencySymbols = [] self.lastProgressBarTime = { } self.lastProgressBarValue = { } self.lastSelectedMenu = None self.attributeNamesDict = { } def setupInputEventHandlers(self): '''Defines InputEventHandler fields for this script that can be called by the key and braille bindings.''' self.inputEventHandlers['leftClickReviewItemHandler'] = input_event.InputEventHandler(Script.leftClickReviewItem, _('Performs left click on current flat review item.')) self.inputEventHandlers['rightClickReviewItemHandler'] = input_event.InputEventHandler(Script.rightClickReviewItem, _('Performs right click on current flat review item.')) self.inputEventHandlers['sayAllHandler'] = input_event.InputEventHandler(Script.sayAll, _('Speaks entire document.')) self.inputEventHandlers['whereAmIBasicHandler'] = input_event.InputEventHandler(Script.whereAmIBasic, _('Performs the basic where am I operation.')) self.inputEventHandlers['whereAmIDetailedHandler'] = input_event.InputEventHandler(Script.whereAmIDetailed, _('Performs the detailed where am I operation.')) self.inputEventHandlers['getTitleHandler'] = input_event.InputEventHandler(Script.getTitle, _('Speaks the title bar.')) self.inputEventHandlers['getStatusBarHandler'] = input_event.InputEventHandler(Script.getStatusBar, _('Speaks the status bar.')) self.inputEventHandlers['findHandler'] = input_event.InputEventHandler(orca.showFindGUI, _('Opens the Orca Find dialog.')) self.inputEventHandlers['findNextHandler'] = input_event.InputEventHandler(Script.findNext, _('Searches for the next instance of a string.')) self.inputEventHandlers['findPreviousHandler'] = input_event.InputEventHandler(Script.findPrevious, _('Searches for the previous instance of a string.')) self.inputEventHandlers['showZonesHandler'] = input_event.InputEventHandler(Script.showZones, _('Paints and prints the visible zones in the active window.')) self.inputEventHandlers['toggleFlatReviewModeHandler'] = input_event.InputEventHandler(Script.toggleFlatReviewMode, _('Enters and exits flat review mode.')) self.inputEventHandlers['reviewPreviousLineHandler'] = input_event.InputEventHandler(Script.reviewPreviousLine, _('Moves flat review to the beginning of the previous line.')) self.inputEventHandlers['reviewHomeHandler'] = input_event.InputEventHandler(Script.reviewHome, _('Moves flat review to the home position.')) self.inputEventHandlers['reviewCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewCurrentLine, _('Speaks the current flat review line.')) self.inputEventHandlers['reviewSpellCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentLine, _('Spells the current flat review line.')) self.inputEventHandlers['reviewPhoneticCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewPhoneticCurrentLine, _('Phonetically spells the current flat review line.')) self.inputEventHandlers['reviewNextLineHandler'] = input_event.InputEventHandler(Script.reviewNextLine, _('Moves flat review to the beginning of the next line.')) self.inputEventHandlers['reviewEndHandler'] = input_event.InputEventHandler(Script.reviewEnd, _('Moves flat review to the end position.')) self.inputEventHandlers['reviewPreviousItemHandler'] = input_event.InputEventHandler(Script.reviewPreviousItem, _('Moves flat review to the previous item or word.')) self.inputEventHandlers['reviewAboveHandler'] = input_event.InputEventHandler(Script.reviewAbove, _('Moves flat review to the word above the current word.')) self.inputEventHandlers['reviewCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewCurrentItem, _('Speaks the current flat review item or word.')) self.inputEventHandlers['reviewSpellCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentItem, _('Spells the current flat review item or word.')) self.inputEventHandlers['reviewPhoneticCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewPhoneticCurrentItem, _('Phonetically spells the current flat review item or word.')) self.inputEventHandlers['reviewCurrentAccessibleHandler'] = input_event.InputEventHandler(Script.reviewCurrentAccessible, _('Speaks the current flat review object.')) self.inputEventHandlers['reviewNextItemHandler'] = input_event.InputEventHandler(Script.reviewNextItem, _('Moves flat review to the next item or word.')) self.inputEventHandlers['reviewBelowHandler'] = input_event.InputEventHandler(Script.reviewBelow, _('Moves flat review to the word below the current word.')) self.inputEventHandlers['reviewPreviousCharacterHandler'] = input_event.InputEventHandler(Script.reviewPreviousCharacter, _('Moves flat review to the previous character.')) self.inputEventHandlers['reviewEndOfLineHandler'] = input_event.InputEventHandler(Script.reviewEndOfLine, _('Moves flat review to the end of the line.')) self.inputEventHandlers['reviewCurrentCharacterHandler'] = input_event.InputEventHandler(Script.reviewCurrentCharacter, _('Speaks the current flat review character.')) self.inputEventHandlers['reviewSpellCurrentCharacterHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentCharacter, _('Phonetically speaks the current flat review character.')) self.inputEventHandlers['reviewNextCharacterHandler'] = input_event.InputEventHandler(Script.reviewNextCharacter, _('Moves flat review to the next character.')) self.inputEventHandlers['toggleTableCellReadModeHandler'] = input_event.InputEventHandler(Script.toggleTableCellReadMode, _('Toggles whether to read just the current table cell or the whole row.')) self.inputEventHandlers['readCharAttributesHandler'] = input_event.InputEventHandler(Script.readCharAttributes, _('Reads the attributes associated with the current text character.')) self.inputEventHandlers['reportScriptInfoHandler'] = input_event.InputEventHandler(Script.reportScriptInfo, _('Reports information on current script.')) self.inputEventHandlers['panBrailleLeftHandler'] = input_event.InputEventHandler(Script.panBrailleLeft, _('Pans the braille display to the left.'), False) self.inputEventHandlers['panBrailleRightHandler'] = input_event.InputEventHandler(Script.panBrailleRight, _('Pans the braille display to the right.'), False) self.inputEventHandlers['reviewBottomLeftHandler'] = input_event.InputEventHandler(Script.reviewBottomLeft, _('Moves flat review to the bottom left.')) self.inputEventHandlers['goBrailleHomeHandler'] = input_event.InputEventHandler(Script.goBrailleHome, _('Returns to object with keyboard focus.')) self.inputEventHandlers['enterLearnModeHandler'] = input_event.InputEventHandler(Script.enterLearnMode, _('Enters learn mode. Press escape to exit learn mode.')) self.inputEventHandlers['decreaseSpeechRateHandler'] = input_event.InputEventHandler(speech.decreaseSpeechRate, _('Decreases the speech rate.')) self.inputEventHandlers['increaseSpeechRateHandler'] = input_event.InputEventHandler(speech.increaseSpeechRate, _('Increases the speech rate.')) self.inputEventHandlers['decreaseSpeechPitchHandler'] = input_event.InputEventHandler(speech.decreaseSpeechPitch, _('Decreases the speech pitch.')) self.inputEventHandlers['increaseSpeechPitchHandler'] = input_event.InputEventHandler(speech.increaseSpeechPitch, _('Increases the speech pitch.')) self.inputEventHandlers['shutdownHandler'] = input_event.InputEventHandler(orca.quitOrca, _('Quits Orca')) self.inputEventHandlers['preferencesSettingsHandler'] = input_event.InputEventHandler(orca.showPreferencesGUI, _('Displays the preferences configuration dialog.')) self.inputEventHandlers['appPreferencesSettingsHandler'] = input_event.InputEventHandler(orca.showAppPreferencesGUI, _('Displays the application preferences configuration dialog.')) self.inputEventHandlers['toggleSilenceSpeechHandler'] = input_event.InputEventHandler(orca.toggleSilenceSpeech, _('Toggles the silencing of speech.')) self.inputEventHandlers['listAppsHandler'] = input_event.InputEventHandler(Script.printAppsHandler, _('Prints a debug listing of all known applications to the console where Orca is running.')) self.inputEventHandlers['cycleDebugLevelHandler'] = input_event.InputEventHandler(orca.cycleDebugLevel, _('Cycles the debug level at run time.')) self.inputEventHandlers['printActiveAppHandler'] = input_event.InputEventHandler(Script.printActiveAppHandler, _('Prints debug information about the currently active application to the console where Orca is running.')) self.inputEventHandlers['printAncestryHandler'] = input_event.InputEventHandler(Script.printAncestryHandler, _('Prints debug information about the ancestry of the object with focus.')) self.inputEventHandlers['printHierarchyHandler'] = input_event.InputEventHandler(Script.printHierarchyHandler, _('Prints debug information about the application with focus.')) self.inputEventHandlers['printMemoryUsageHandler'] = input_event.InputEventHandler(Script.printMemoryUsageHandler, _('Prints memory usage information.')) self.inputEventHandlers['bookmarkCurrentWhereAmI'] = input_event.InputEventHandler(Script.bookmarkCurrentWhereAmI, _('Bookmark where am I with respect to current position.')) self.inputEventHandlers['goToBookmark'] = input_event.InputEventHandler(Script.goToBookmark, _('Go to bookmark.')) self.inputEventHandlers['addBookmark'] = input_event.InputEventHandler(Script.addBookmark, _('Add bookmark.')) self.inputEventHandlers['saveBookmarks'] = input_event.InputEventHandler(Script.saveBookmarks, _('Save bookmarks.')) self.inputEventHandlers['goToNextBookmark'] = input_event.InputEventHandler(Script.goToNextBookmark, _('Go to next bookmark location.')) self.inputEventHandlers['goToPrevBookmark'] = input_event.InputEventHandler(Script.goToPrevBookmark, _('Go to previous bookmark location.')) self.inputEventHandlers['toggleColorEnhancementsHandler'] = input_event.InputEventHandler(mag.toggleColorEnhancements, _('Toggles color enhancements.')) self.inputEventHandlers['toggleMouseEnhancementsHandler'] = input_event.InputEventHandler(mag.toggleMouseEnhancements, _('Toggles mouse enhancements.')) self.inputEventHandlers['increaseMagnificationHandler'] = input_event.InputEventHandler(mag.increaseMagnification, _('Increases the magnification level.')) self.inputEventHandlers['decreaseMagnificationHandler'] = input_event.InputEventHandler(mag.decreaseMagnification, _('Decreases the magnification level.')) self.inputEventHandlers['toggleMagnifierHandler'] = input_event.InputEventHandler(mag.toggleMagnifier, _('Toggles the magnifier.')) self.inputEventHandlers['cycleZoomerTypeHandler'] = input_event.InputEventHandler(mag.cycleZoomerType, _('Cycles to the next magnifier position.')) self.inputEventHandlers['toggleMouseReviewHandler'] = input_event.InputEventHandler(mouse_review.toggle, _('Toggle mouse review mode.')) self.inputEventHandlers['bypassNextCommandHandler'] = input_event.InputEventHandler(Script.bypassNextCommand, _('Passes the next command on to the current application.')) def getInputEventHandlerKey(self, inputEventHandler): '''Returns the name of the key that contains an inputEventHadler passed as argument ''' for keyName, handler in self.inputEventHandlers.iteritems(): if handler == inputEventHandler: return keyName def getListeners(self): '''Sets up the AT-SPI event listeners for this script. ''' listeners = script.Script.getListeners(self) listeners['focus:'] = self.onFocus listeners['mouse:button'] = self.onMouseButton listeners['object:property-change:accessible-name'] = self.onNameChanged listeners['object:text-caret-moved'] = self.onCaretMoved listeners['object:text-changed:delete'] = self.onTextDeleted listeners['object:text-changed:insert'] = self.onTextInserted listeners['object:active-descendant-changed'] = self.onActiveDescendantChanged listeners['object:link-selected'] = self.onLinkSelected listeners['object:state-changed:active'] = self.onStateChanged listeners['object:state-changed:focused'] = self.onStateChanged listeners['object:state-changed:showing'] = self.onStateChanged listeners['object:state-changed:checked'] = self.onStateChanged listeners['object:state-changed:pressed'] = self.onStateChanged listeners['object:state-changed:indeterminate'] = self.onStateChanged listeners['object:state-changed:expanded'] = self.onStateChanged listeners['object:state-changed:selected'] = self.onStateChanged listeners['object:text-selection-changed'] = self.onTextSelectionChanged listeners['object:selection-changed'] = self.onSelectionChanged listeners['object:property-change:accessible-value'] = self.onValueChanged listeners['object:value-changed'] = self.onValueChanged listeners['window:activate'] = self.onWindowActivated listeners['window:deactivate'] = self.onWindowDeactivated listeners['window:create'] = self.noOp return listeners def __getDesktopBindings(self): '''Returns an instance of keybindings.KeyBindings that use the numeric keypad for focus tracking and flat review. ''' keyBindings = keybindings.KeyBindings() keyBindings.add(keybindings.KeyBinding('KP_Divide', settings.NO_MODIFIER_MASK, settings.NO_MODIFIER_MASK, self.inputEventHandlers['leftClickReviewItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Multiply', settings.NO_MODIFIER_MASK, settings.NO_MODIFIER_MASK, self.inputEventHandlers['rightClickReviewItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Subtract', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleFlatReviewModeHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Add', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['sayAllHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['whereAmIBasicHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['whereAmIDetailedHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getTitleHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getStatusBarHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['findHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findNextHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.ORCA_SHIFT_MODIFIER_MASK, self.inputEventHandlers['findPreviousHandler'])) keyBindings.add(keybindings.KeyBinding('KP_7', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Home', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_7', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Home', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler'])) keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3)) keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3)) keyBindings.add(keybindings.KeyBinding('KP_9', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Page_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_9', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Page_Up', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler'])) keyBindings.add(keybindings.KeyBinding('KP_4', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Left', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_4', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Left', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler'])) keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3)) keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3)) keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler'])) keyBindings.add(keybindings.KeyBinding('KP_6', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Right', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler'])) keyBindings.add(keybindings.KeyBinding('KP_6', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Right', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler'])) keyBindings.add(keybindings.KeyBinding('KP_1', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler'])) keyBindings.add(keybindings.KeyBinding('KP_End', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler'])) keyBindings.add(keybindings.KeyBinding('KP_1', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_End', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler'])) keyBindings.add(keybindings.KeyBinding('KP_2', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_2', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1)) keyBindings.add(keybindings.KeyBinding('KP_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2)) keyBindings.add(keybindings.KeyBinding('KP_3', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler'])) keyBindings.add(keybindings.KeyBinding('KP_Page_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler'])) return keyBindings def __getLaptopBindings(self): '''Returns an instance of keybindings.KeyBindings that use the the main keyboard keys for focus tracking and flat review. ''' keyBindings = keybindings.KeyBindings() keyBindings.add(keybindings.KeyBinding('7', settings.ORCA_MODIFIER_MASK, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['leftClickReviewItemHandler'])) keyBindings.add(keybindings.KeyBinding('8', settings.ORCA_MODIFIER_MASK, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['rightClickReviewItemHandler'])) keyBindings.add(keybindings.KeyBinding('p', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleFlatReviewModeHandler'])) keyBindings.add(keybindings.KeyBinding('semicolon', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['sayAllHandler'])) keyBindings.add(keybindings.KeyBinding('Return', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['whereAmIBasicHandler'], 1)) keyBindings.add(keybindings.KeyBinding('Return', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['whereAmIDetailedHandler'], 2)) keyBindings.add(keybindings.KeyBinding('slash', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getTitleHandler'], 1)) keyBindings.add(keybindings.KeyBinding('slash', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getStatusBarHandler'], 2)) keyBindings.add(keybindings.KeyBinding('bracketleft', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findHandler'])) keyBindings.add(keybindings.KeyBinding('bracketright', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findNextHandler'])) keyBindings.add(keybindings.KeyBinding('bracketright', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['findPreviousHandler'])) keyBindings.add(keybindings.KeyBinding('u', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler'])) keyBindings.add(keybindings.KeyBinding('u', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler'])) keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1)) keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2)) keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3)) keyBindings.add(keybindings.KeyBinding('o', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler'])) keyBindings.add(keybindings.KeyBinding('o', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler'])) keyBindings.add(keybindings.KeyBinding('j', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler'])) keyBindings.add(keybindings.KeyBinding('j', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler'])) keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1)) keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2)) keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3)) keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler'])) keyBindings.add(keybindings.KeyBinding('l', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler'])) keyBindings.add(keybindings.KeyBinding('l', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler'])) keyBindings.add(keybindings.KeyBinding('m', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler'])) keyBindings.add(keybindings.KeyBinding('m', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler'])) keyBindings.add(keybindings.KeyBinding('comma', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1)) keyBindings.add(keybindings.KeyBinding('comma', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2)) keyBindings.add(keybindings.KeyBinding('period', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler'])) return keyBindings def getKeyBindings(self): '''Defines the key bindings for this script. Returns an instance of keybindings.KeyBindings. ''' keyBindings = script.Script.getKeyBindings(self) if settings.keyboardLayout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP: for keyBinding in self._Script__getDesktopBindings().keyBindings: keyBindings.add(keyBinding) else: for keyBinding in self._Script__getLaptopBindings().keyBindings: keyBindings.add(keyBinding) keyBindings.add(keybindings.KeyBinding('Num_Lock', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['showZonesHandler'])) keyBindings.add(keybindings.KeyBinding('F11', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleTableCellReadModeHandler'])) keyBindings.add(keybindings.KeyBinding('SunF36', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleTableCellReadModeHandler'])) keyBindings.add(keybindings.KeyBinding('f', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['readCharAttributesHandler'])) keyBindings.add(keybindings.KeyBinding('h', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['enterLearnModeHandler'])) keyBindings.add(keybindings.KeyBinding('q', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['shutdownHandler'])) keyBindings.add(keybindings.KeyBinding('space', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['preferencesSettingsHandler'])) keyBindings.add(keybindings.KeyBinding('space', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['appPreferencesSettingsHandler'])) keyBindings.add(keybindings.KeyBinding('s', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleSilenceSpeechHandler'])) keyBindings.add(keybindings.KeyBinding('End', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['listAppsHandler'])) keyBindings.add(keybindings.KeyBinding('Home', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['reportScriptInfoHandler'])) keyBindings.add(keybindings.KeyBinding('Page_Up', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['printAncestryHandler'])) keyBindings.add(keybindings.KeyBinding('Page_Down', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['printHierarchyHandler'])) keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_ALT_MODIFIER_MASK, self.inputEventHandlers['saveBookmarks'])) keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_SHIFT_MODIFIER_MASK, self.inputEventHandlers['goToPrevBookmark'])) keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['goToNextBookmark'])) for key in xrange(1, 7): keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.ORCA_ALT_MODIFIER_MASK, self.inputEventHandlers['addBookmark'])) keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['goToBookmark'])) keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.SHIFT_ALT_MODIFIER_MASK, self.inputEventHandlers['bookmarkCurrentWhereAmI'])) keyBindings.add(keybindings.KeyBinding('BackSpace', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['bypassNextCommandHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reportScriptInfoHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['cycleDebugLevelHandler'])) if settings.debugMemoryUsage: keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['printMemoryUsageHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseSpeechRateHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseSpeechRateHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseSpeechPitchHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseSpeechPitchHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleColorEnhancementsHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMouseEnhancementsHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseMagnificationHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseMagnificationHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMagnifierHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['cycleZoomerTypeHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['panBrailleLeftHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['panBrailleRightHandler'])) keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMouseReviewHandler'])) keyBindings = settings.overrideKeyBindings(self, keyBindings) return keyBindings def getBrailleBindings(self): '''Defines the braille bindings for this script. Returns a dictionary where the keys are BrlTTY commands and the values are InputEventHandler instances. ''' brailleBindings = script.Script.getBrailleBindings(self) brailleBindings[braille.CMD_FWINLT] = self.inputEventHandlers['panBrailleLeftHandler'] brailleBindings[braille.CMD_FWINRT] = self.inputEventHandlers['panBrailleRightHandler'] brailleBindings[braille.CMD_LNUP] = self.inputEventHandlers['reviewAboveHandler'] brailleBindings[braille.CMD_LNDN] = self.inputEventHandlers['reviewBelowHandler'] brailleBindings[braille.CMD_FREEZE] = self.inputEventHandlers['toggleFlatReviewModeHandler'] brailleBindings[braille.CMD_TOP_LEFT] = self.inputEventHandlers['reviewHomeHandler'] brailleBindings[braille.CMD_BOT_LEFT] = self.inputEventHandlers['reviewBottomLeftHandler'] brailleBindings[braille.CMD_HOME] = self.inputEventHandlers['goBrailleHomeHandler'] return brailleBindings def processKeyboardEvent(self, keyboardEvent): '''Processes the given keyboard event. It uses the super class equivalent to do most of the work. The only thing done here is to detect when the user is trying to get out of learn mode. Arguments: - keyboardEvent: an instance of input_event.KeyboardEvent ''' return script.Script.processKeyboardEvent(self, keyboardEvent) def __sayAllProgressCallback(self, context, progressType): text = context.obj.queryText() if progressType == speechserver.SayAllContext.PROGRESS: return None if progressType == speechserver.SayAllContext.INTERRUPTED: text.setCaretOffset(context.currentOffset) elif progressType == speechserver.SayAllContext.COMPLETED: orca.setLocusOfFocus(None, context.obj, False) text.setCaretOffset(context.currentOffset) if text.getNSelections(): text.setSelection(0, context.currentOffset, context.currentOffset) def sayAll(self, inputEvent): clickCount = self.getClickCount() doubleClick = clickCount == 2 self.lastSayAllEvent = inputEvent if doubleClick: context = self.getFlatReviewContext() utterances = [] context.goBegin() while True: (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.ZONE) utterances.append(wordString) moved = context.goNext(flat_review.Context.ZONE, flat_review.Context.WRAP_LINE) if not moved: break continue speech.speakUtterances(utterances) elif self.isTextArea(orca_state.locusOfFocus): try: orca_state.locusOfFocus.queryText() except NotImplementedError: utterances = self.speechGenerator.getSpeech(orca_state.locusOfFocus, False) utterances.extend(self.tutorialGenerator.getTutorial(orca_state.locusOfFocus, False)) speech.speakUtterances(utterances) except AttributeError: pass speech.sayAll(self.textLines(orca_state.locusOfFocus), self._Script__sayAllProgressCallback) return True def isTextArea(self, obj): '''Returns True if obj is a GUI component that is for entering text. Arguments: - obj: an accessible ''' if obj: pass return obj.getRole() in (pyatspi.ROLE_TEXT, pyatspi.ROLE_PARAGRAPH, pyatspi.ROLE_TERMINAL) def isReadOnlyTextArea(self, obj): '''Returns True if obj is a text entry area that is read only.''' state = obj.getState() if self.isTextArea(obj) and state.contains(pyatspi.STATE_FOCUSABLE): pass readOnly = not state.contains(pyatspi.STATE_EDITABLE) debug.println(debug.LEVEL_ALL, 'default.py:isReadOnlyTextArea=%s for %s' % (readOnly, debug.getAccessibleDetails(obj))) return readOnly def getText(self, obj, startOffset, endOffset): """Returns the substring of the given object's text specialization. Arguments: - obj: an accessible supporting the accessible text specialization - startOffset: the starting character position - endOffset: the ending character position """ return obj.queryText().getText(startOffset, endOffset) def sayPhrase(self, obj, startOffset, endOffset): """Speaks the text of an Accessible object between the start and end offsets, unless the phrase is empty in which case it's ignored. Arguments: - obj: an Accessible object that implements the AccessibleText interface - startOffset: the start text offset. - endOffset: the end text offset. """ phrase = self.getText(obj, startOffset, endOffset) if len(phrase) and phrase != '\n': if phrase.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] phrase = self.adjustForRepeats(phrase) speech.speak(phrase, voice) else: self.sayCharacter(obj) def sayLine(self, obj): """Speaks the line of an AccessibleText object that contains the caret, unless the line is empty in which case it's ignored. Arguments: - obj: an Accessible object that implements the AccessibleText interface """ (line, caretOffset, startOffset) = self.getTextLineAtCaret(obj) debug.println(debug.LEVEL_FINEST, 'sayLine: line=<%s>, len=%d, start=%d, ' % (line, len(line), startOffset)) debug.println(debug.LEVEL_FINEST, 'caret=%d, speakBlankLines=%s' % (caretOffset, settings.speakBlankLines)) if len(line) and line != '\n': if line.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] if settings.enableSpeechIndentation: self.speakTextIndentation(obj, line) line = self.adjustForLinks(obj, line, startOffset) line = self.adjustForRepeats(line) speech.speak(line, voice) else: self.sayCharacter(obj) def sayWord(self, obj): '''Speaks the word at the caret. [[[TODO: WDW - what if there is no word at the caret?]]] Arguments: - obj: an Accessible object that implements the AccessibleText interface ''' text = obj.queryText() offset = text.caretOffset lastKey = orca_state.lastNonModifierKeyEvent.event_string lastWord = orca_state.lastWord (word, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_WORD_START) if lastKey == 'Right' and len(lastWord) > 0: lastChar = lastWord[len(lastWord) - 1] if lastChar == '\n' and lastWord != word: voice = self.voices[settings.DEFAULT_VOICE] speech.speakCharacter('\n', voice) if lastKey == 'Left' and len(word) > 0: lastChar = word[len(word) - 1] if lastChar == '\n' and lastWord != word: voice = self.voices[settings.DEFAULT_VOICE] speech.speakCharacter('\n', voice) if self.getLinkIndex(obj, offset) >= 0: voice = self.voices[settings.HYPERLINK_VOICE] elif word.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] word = self.adjustForRepeats(word) orca_state.lastWord = word speech.speak(word, voice) def speakTextIndentation(self, obj, line): '''Speaks a summary of the number of spaces and/or tabs at the beginning of the given line. Arguments: - obj: the text object. - line: the string to check for spaces and tabs. ''' line = line.replace('\xc2\xa0', ' ') line = line.decode('UTF-8') spaceCount = 0 tabCount = 0 utterance = '' offset = 0 while True: while offset < len(line) and line[offset] == ' ': spaceCount += 1 offset += 1 if spaceCount: utterance += ngettext('%d space', '%d spaces', spaceCount) % spaceCount + ' ' while offset < len(line) and line[offset] == '\t': tabCount += 1 offset += 1 if tabCount: utterance += ngettext('%d tab', '%d tabs', tabCount) % tabCount + ' ' if not spaceCount or tabCount: break spaceCount = tabCount = 0 if len(utterance): speech.speak(utterance) def echoPreviousSentence(self, obj): """Speaks the sentence prior to the caret, as long as there is a sentence prior to the caret and there is no intervening sentence delimiter between the caret and the end of the sentence. The entry condition for this method is that the character prior to the current caret position is a sentence delimiter, and it's what caused this method to be called in the first place. Arguments: - obj: an Accessible object that implements the AccessibleText interface. """ try: text = obj.queryText() except NotImplementedError: return None offset = text.caretOffset - 1 previousOffset = text.caretOffset - 2 if offset < 0 or previousOffset < 0: return None (currentChar, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR) (previousChar, startOffset, endOffset) = text.getTextAtOffset(previousOffset, pyatspi.TEXT_BOUNDARY_CHAR) if not self.isSentenceDelimiter(currentChar, previousChar): return None sentenceEndOffset = text.caretOffset - 2 sentenceStartOffset = sentenceEndOffset while sentenceStartOffset >= 0: (currentChar, startOffset, endOffset) = text.getTextAtOffset(sentenceStartOffset, pyatspi.TEXT_BOUNDARY_CHAR) (previousChar, startOffset, endOffset) = text.getTextAtOffset(sentenceStartOffset - 1, pyatspi.TEXT_BOUNDARY_CHAR) if self.isSentenceDelimiter(currentChar, previousChar): break continue self.isSentenceDelimiter(currentChar, previousChar) sentenceStartOffset -= 1 continue previousOffset < 0 if sentenceStartOffset == sentenceEndOffset: return None sentence = self.getText(obj, sentenceStartOffset + 1, sentenceEndOffset + 1) if self.getLinkIndex(obj, sentenceStartOffset + 1) >= 0: voice = self.voices[settings.HYPERLINK_VOICE] elif sentence.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] sentence = self.adjustForRepeats(sentence) speech.speak(sentence, voice) def echoPreviousWord(self, obj, offset = None): """Speaks the word prior to the caret, as long as there is a word prior to the caret and there is no intervening word delimiter between the caret and the end of the word. The entry condition for this method is that the character prior to the current caret position is a word delimiter, and it's what caused this method to be called in the first place. Arguments: - obj: an Accessible object that implements the AccessibleText interface. - offset: if not None, the offset within the text to use as the end of the word. """ try: text = obj.queryText() except NotImplementedError: return None if not offset: offset = text.caretOffset - 1 if offset < 0: return None (char, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR) if not self.isWordDelimiter(char): return None wordEndOffset = offset - 1 wordStartOffset = wordEndOffset while wordStartOffset >= 0: (char, startOffset, endOffset) = text.getTextAtOffset(wordStartOffset, pyatspi.TEXT_BOUNDARY_CHAR) if self.isWordDelimiter(char): break continue self.isWordDelimiter(char) wordStartOffset -= 1 continue offset < 0 if wordStartOffset == wordEndOffset: return None word = self.getText(obj, wordStartOffset + 1, wordEndOffset + 1) if self.getLinkIndex(obj, wordStartOffset + 1) >= 0: voice = self.voices[settings.HYPERLINK_VOICE] elif word.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] word = self.adjustForRepeats(word) speech.speak(word, voice) def sayCharacter(self, obj): '''Speak the character at the caret. Arguments: - obj: an Accessible object that implements the AccessibleText interface ''' text = obj.queryText() offset = text.caretOffset try: mods = orca_state.lastInputEvent.modifiers eventString = orca_state.lastInputEvent.event_string except: mods = 0 eventString = '' if mods & settings.SHIFT_MODIFIER_MASK and eventString in ('Right', 'Down'): offset -= 1 (character, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR) if not character: character = '\n' if self.getLinkIndex(obj, offset) >= 0: voice = self.voices[settings.HYPERLINK_VOICE] elif character.decode('UTF-8').isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] debug.println(debug.LEVEL_FINEST, 'sayCharacter: char=<%s>, startOffset=%d, ' % (character, startOffset)) debug.println(debug.LEVEL_FINEST, 'caretOffset=%d, endOffset=%d, speakBlankLines=%s' % (offset, endOffset, settings.speakBlankLines)) if character == '\n': line = text.getTextAtOffset(max(0, offset), pyatspi.TEXT_BOUNDARY_LINE_START) if not line[0] or line[0] == '\n': if settings.speakBlankLines: speech.speak(_('blank'), voice, False) return None speech.speakCharacter(character, voice) def isFunctionalDialog(self, obj): '''Returns true if the window is a functioning as a dialog. This method should be subclassed by application scripts as needed. ''' return False def getUnfocusedAlertAndDialogCount(self, obj): '''If the current application has one or more alert or dialog windows and the currently focused window is not an alert or a dialog, return a count of the number of alert and dialog windows, otherwise return a count of zero. Arguments: - obj: the Accessible object Returns the alert and dialog count. ''' alertAndDialogCount = 0 app = obj.getApplication() window = self.getTopLevel(obj) if window and window.getRole() != pyatspi.ROLE_ALERT and window.getRole() != pyatspi.ROLE_DIALOG and not self.isFunctionalDialog(window): for child in app: if child.getRole() == pyatspi.ROLE_ALERT and child.getRole() == pyatspi.ROLE_DIALOG or self.isFunctionalDialog(child): alertAndDialogCount += 1 continue return alertAndDialogCount def presentTooltip(self, obj): ''' Speaks the tooltip for the current object of interest. ''' text = '' if obj.description: text = obj.description else: text = self.whereAmI.getObjLabelAndName(obj) debug.println(debug.LEVEL_FINEST, "presentTooltip: text='%s'" % text) if text != '': braille.displayMessage(text) speech.speak(text) def doWhereAmI(self, inputEvent, basicOnly): '''Peforms the whereAmI operation. Arguments: - inputEvent: The original inputEvent ''' obj = orca_state.locusOfFocus self.updateBraille(obj) return self.whereAmI.whereAmI(obj, basicOnly) def whereAmIBasic(self, inputEvent): '''Speaks basic information about the current object of interest. ''' self.doWhereAmI(inputEvent, True) def whereAmIDetailed(self, inputEvent): '''Speaks detailed/custom information about the current object of interest. ''' self.doWhereAmI(inputEvent, False) def getTitle(self, inputEvent): '''Speaks the title of the window with focus. ''' obj = orca_state.locusOfFocus self.updateBraille(obj) return self.whereAmI.speakTitle(orca_state.locusOfFocus) def getStatusBar(self, inputEvent): '''Speaks the contents of the status bar of the window with focus. ''' obj = orca_state.locusOfFocus self.updateBraille(obj) return self.whereAmI.speakStatusBar(orca_state.locusOfFocus) def findCommonAncestor(self, a, b): '''Finds the common ancestor between Accessible a and Accessible b. Arguments: - a: Accessible - b: Accessible ''' debug.println(debug.LEVEL_FINEST, 'default.findCommonAncestor...') if not a or not b: return None if a == b: return a aParents = [ a] try: parent = a.parent while parent and parent.parent != parent: aParents.append(parent) parent = parent.parent continue a == b aParents.reverse() except: a == b not b debug.printException(debug.LEVEL_FINEST) bParents = [ b] try: parent = b.parent while parent and parent.parent != parent: bParents.append(parent) parent = parent.parent continue a == b bParents.reverse() except: a == b not b debug.printException(debug.LEVEL_FINEST) commonAncestor = None maxSearch = min(len(aParents), len(bParents)) i = 0 while i < maxSearch: if self.isSameObject(aParents[i], bParents[i]): commonAncestor = aParents[i] i += 1 continue a == b break continue not b debug.println(debug.LEVEL_FINEST, '...default.findCommonAncestor') return commonAncestor def handleProgressBarUpdate(self, event, obj): '''Determine whether this progress bar event should be spoken or not. It should be spoken if: 1/ settings.enableProgressBarUpdates is True. 2/ The application with the progress bar has focus. 3/ The time of this event exceeds the settings.progressBarUpdateInterval value. This value indicates the time (in seconds) between potential spoken progress bar updates. 4/ The new value of the progress bar (converted to an integer), is different from the last one or equals 100 (i.e complete). Arguments: - event: if not None, the Event that caused this to happen - obj: the Accessible progress bar object. ''' if settings.enableProgressBarUpdates: if orca_state.locusOfFocus and orca_state.locusOfFocus.getApplication() == obj.getApplication(): currentTime = time.time() defunctBars = 0 mostRecentUpdate = [ obj, 0] for key, value in self.lastProgressBarTime.items(): if value > mostRecentUpdate[1]: mostRecentUpdate = [ key, value] try: isDefunct = key.getState().contains(pyatspi.STATE_DEFUNCT) except: isDefunct = True if isDefunct: defunctBars += 1 continue if defunctBars == len(self.lastProgressBarTime): self.lastProgressBarTime = { } self.lastProgressBarValue = { } if obj not in self.lastProgressBarTime: self.lastProgressBarTime[obj] = 0 if obj not in self.lastProgressBarValue: self.lastProgressBarValue[obj] = None lastProgressBarTime = self.lastProgressBarTime[obj] lastProgressBarValue = self.lastProgressBarValue[obj] value = obj.queryValue() percentValue = int((value.currentValue / (value.maximumValue - value.minimumValue)) * 100) if currentTime - lastProgressBarTime > settings.progressBarUpdateInterval or percentValue == 100: if lastProgressBarValue != percentValue: utterances = [] if len(self.lastProgressBarTime) > 1: index = 0 for key in self.lastProgressBarTime.keys(): if key == obj and key != mostRecentUpdate[0]: label = _('Progress bar %d.') % (index + 1) utterances.append(label) continue index += 1 percentage = _('%d percent.') % percentValue + ' ' utterances.append(percentage) speech.speakUtterances(utterances) self.lastProgressBarTime[obj] = currentTime self.lastProgressBarValue[obj] = percentValue def locusOfFocusChanged(self, event, oldLocusOfFocus, newLocusOfFocus): '''Called when the visual object with focus changes. Arguments: - event: if not None, the Event that caused the change - oldLocusOfFocus: Accessible that is the old locus of focus - newLocusOfFocus: Accessible that is the new locus of focus ''' if newLocusOfFocus and newLocusOfFocus.getState().contains(pyatspi.STATE_DEFUNCT): return None try: if self.findCommandRun: return None except: newLocusOfFocus.getState().contains(pyatspi.STATE_DEFUNCT) if newLocusOfFocus: mag.magnifyAccessible(event, newLocusOfFocus) if self.flatReviewContext: self.toggleFlatReviewMode() if self.isSameObject(oldLocusOfFocus, newLocusOfFocus): return None if oldLocusOfFocus: oldParent = oldLocusOfFocus.parent else: oldParent = None if newLocusOfFocus: newParent = newLocusOfFocus.parent else: newParent = None if oldParent is not None and oldParent == newParent and newParent.getRole() == pyatspi.ROLE_TABLE: for key in self.pointOfReference.keys(): if key not in ('lastRow', 'lastColumn'): del self.pointOfReference[key] continue else: self.pointOfReference = { } if newLocusOfFocus: self.updateBraille(newLocusOfFocus) utterances = [] commonAncestor = self.findCommonAncestor(oldLocusOfFocus, newLocusOfFocus) if commonAncestor: context = self.speechGenerator.getSpeechContext(newLocusOfFocus, commonAncestor) utterances.append(' '.join(context)) oldNodeLevel = -1 newNodeLevel = -1 if newLocusOfFocus.getRole() == pyatspi.ROLE_TABLE_CELL or newParent.getRole() == pyatspi.ROLE_TABLE: try: table = oldParent.queryTable() except: table = None if table: if oldLocusOfFocus.getRole() == pyatspi.ROLE_TABLE_CELL or oldParent.getRole() == pyatspi.ROLE_TABLE: index = self.getCellIndex(oldLocusOfFocus) oldRow = table.getRowAtIndex(index) oldCol = table.getColumnAtIndex(index) else: oldRow = -1 oldCol = -1 try: table = newParent.queryTable() except: pass index = self.getCellIndex(newLocusOfFocus) newRow = table.getRowAtIndex(index) newCol = table.getColumnAtIndex(index) if newRow >= 0: if newRow != oldRow or oldParent != newParent: desc = table.getRowDescription(newRow) if not desc: header = table.getRowHeader(newRow) if header: desc = self.getDisplayedText(header) if desc and len(desc): text = desc if settings.speechVerbosityLevel == settings.VERBOSITY_LEVEL_VERBOSE: text += ' ' + rolenames.rolenames[pyatspi.ROLE_ROW_HEADER].speech utterances.append(text) if newCol >= 0: pass None if newCol != oldCol or oldParent != newParent else topName.endswith(' - Thunderbird') oldNodeLevel = self.getNodeLevel(oldLocusOfFocus) newNodeLevel = self.getNodeLevel(newLocusOfFocus) if newLocusOfFocus and newLocusOfFocus.getRole() == pyatspi.ROLE_RADIO_BUTTON: radioGroupLabel = None inSameGroup = False relations = newLocusOfFocus.getRelationSet() for relation in relations: if not radioGroupLabel and relation.getRelationType() == pyatspi.RELATION_LABELLED_BY: radioGroupLabel = relation.getTarget(0) if not inSameGroup and relation.getRelationType() == pyatspi.RELATION_MEMBER_OF: for i in range(0, relation.getNTargets()): target = relation.getTarget(i) if target == oldLocusOfFocus: inSameGroup = True break continue if not inSameGroup and radioGroupLabel: utterances.append(self.getDisplayedText(radioGroupLabel)) rolesList = [ pyatspi.ROLE_TABLE_CELL, pyatspi.ROLE_TABLE, pyatspi.ROLE_SCROLL_PANE, pyatspi.ROLE_PANEL, pyatspi.ROLE_PANEL] if self.isDesiredFocusedItem(newLocusOfFocus, rolesList) and newLocusOfFocus.getApplication().name == 'orca': orca_state.usePronunciationDictionary = False else: orca_state.usePronunciationDictionary = True utterances.extend(self.speechGenerator.getSpeech(newLocusOfFocus, False)) utterances.extend(self.tutorialGenerator.getTutorial(newLocusOfFocus, False)) if oldNodeLevel != newNodeLevel and newNodeLevel >= 0: utterances.append(_('tree level %d') % (newNodeLevel + 1)) checkIfSelected = False (objRole, parentRole, state) = (None, None, None) if newLocusOfFocus: objRole = newLocusOfFocus.getRole() state = newLocusOfFocus.getState() if newLocusOfFocus.parent: parentRole = newLocusOfFocus.parent.getRole() if objRole == pyatspi.ROLE_TABLE_CELL: if parentRole == pyatspi.ROLE_TREE_TABLE or parentRole == pyatspi.ROLE_TABLE: checkIfSelected = True if checkIfSelected == True and orca_state.lastNonModifierKeyEvent: if orca_state.lastNonModifierKeyEvent.event_string == 'Left' or orca_state.lastNonModifierKeyEvent.event_string == 'Right': checkIfSelected = False if objRole == pyatspi.ROLE_ICON and parentRole == pyatspi.ROLE_LAYERED_PANE: checkIfSelected = True if checkIfSelected and state and not state.contains(pyatspi.STATE_SELECTED): utterances.append(C_('tablecell', ' not selected')) if event and event.type.startswith('focus:') and self.windowActivateTime: pass shouldNotInterrupt = time.time() - self.windowActivateTime < 1 if objRole == pyatspi.ROLE_LINK: voice = self.voices[settings.HYPERLINK_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] speech.speakUtterances(utterances, voice, not shouldNotInterrupt) if objRole == pyatspi.ROLE_TABLE_CELL: try: table = newParent.queryTable() except: pass index = self.getCellIndex(newLocusOfFocus) column = table.getColumnAtIndex(index) self.pointOfReference['lastColumn'] = column row = table.getRowAtIndex(index) self.pointOfReference['lastRow'] = row else: orca_state.noFocusTimeStamp = time.time() def visualAppearanceChanged(self, event, obj): """Called when the visual appearance of an object changes. This method should not be called for objects whose visual appearance changes solely because of focus -- setLocusOfFocus is used for that. Instead, it is intended mostly for objects whose notional 'value' has changed, such as a checkbox changing state, a progress bar advancing, a slider moving, text inserted, caret moved, etc. Arguments: - event: if not None, the Event that caused this to happen - obj: the Accessible whose visual appearance changed. """ if obj.getRole() == pyatspi.ROLE_PROGRESS_BAR: self.handleProgressBarUpdate(event, obj) if self.flatReviewContext: if self.isSameObject(obj, self.flatReviewContext.getCurrentAccessible()): self.updateBrailleReview() return None if False and obj.getRole() == pyatspi.ROLE_PANEL and event.detail1 == 1 and self.isInActiveApp(obj): reallyShowing = True parent = obj.parent while reallyShowing and parent and parent != parent.parent and parent.getRole() != pyatspi.ROLE_APPLICATION: debug.println(debug.LEVEL_FINEST, 'default.visualAppearanceChanged - ' + 'checking parent') reallyShowing = parent.getState().contains(pyatspi.STATE_SHOWING) parent = parent.parent continue self.flatReviewContext if reallyShowing: utterances = [] labels = self.findUnrelatedLabels(obj) for label in labels: utterances.append(label.name) speech.speakUtterances(utterances) return None relations = obj.getRelationSet() for relation in relations: if relation.getRelationType() == pyatspi.RELATION_CONTROLLED_BY: target = relation.getTarget(0) if target == orca_state.locusOfFocus: self.updateBraille(target) utterances = self.speechGenerator.getSpeech(target, True) utterances.extend(self.tutorialGenerator.getTutorial(target, True)) speech.speakUtterances(utterances) return None continue target == orca_state.locusOfFocus if obj.getRole() == pyatspi.ROLE_LABEL and obj.getState().contains(pyatspi.STATE_SHOWING): for relation in relations: if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR: target = relation.getTarget(0) if target == orca_state.locusOfFocus: self.updateBraille(target) utterances = self.speechGenerator.getSpeech(target, True) utterances.extend(self.tutorialGenerator.getTutorial(target, True)) speech.speakUtterances(utterances) return None continue target == orca_state.locusOfFocus if not self.isSameObject(obj, orca_state.locusOfFocus): return None if obj.getRole() == pyatspi.ROLE_RADIO_BUTTON: if orca_state.lastNonModifierKeyEvent and orca_state.lastNonModifierKeyEvent.event_string == 'space': pass else: return None orca_state.lastNonModifierKeyEvent.event_string == 'space' if event: debug.println(debug.LEVEL_FINE, "VISUAL CHANGE: '%s' '%s' (event='%s')" % (obj.name, obj.getRole(), event.type)) else: debug.println(debug.LEVEL_FINE, "VISUAL CHANGE: '%s' '%s' (event=None)" % (obj.name, obj.getRole())) mag.magnifyAccessible(event, obj) self.updateBraille(obj) utterances = self.speechGenerator.getSpeech(obj, True) utterances.extend(self.tutorialGenerator.getTutorial(obj, True)) speech.speakUtterances(utterances) def updateBraille(self, obj, extraRegion = None): '''Updates the braille display to show the give object. Arguments: - obj: the Accessible - extra: extra Region to add to the end ''' if not obj: return None braille.clear() line = braille.Line() braille.addLine(line) try: text = obj.queryText() except NotImplementedError: obj obj text = None except: obj if text and self.isTextArea(obj): (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START) if startOffset == 0: line.addRegions(self.brailleGenerator.getBrailleContext(obj)) else: line.addRegions(self.brailleGenerator.getBrailleContext(obj)) result = self.brailleGenerator.getBrailleRegions(obj) line.addRegions(result[0]) if extraRegion: line.addRegion(extraRegion) if extraRegion: braille.setFocus(extraRegion) else: braille.setFocus(result[1]) braille.refresh(True) def onFocus(self, event): '''Called whenever an object gets focus. Arguments: - event: the Event ''' role = event.source.getRole() if role in (pyatspi.ROLE_MENU, pyatspi.ROLE_MENU_ITEM, pyatspi.ROLE_CHECK_MENU_ITEM, pyatspi.ROLE_RADIO_MENU_ITEM): try: if event.source.querySelection().nSelectedChildren > 0: return None newFocus = event.source if role in (pyatspi.ROLE_LAYERED_PANE, pyatspi.ROLE_TABLE, pyatspi.ROLE_TREE_TABLE, pyatspi.ROLE_TREE): if event.source.childCount: if 'activeDescendantInfo' in self.pointOfReference: (parent, index) = self.pointOfReference['activeDescendantInfo'] newFocus = parent[index] else: try: selection = event.source.querySelection() except NotImplementedError: selection = None if selection and selection.nSelectedChildren > 0: newFocus = selection.getSelectedChild(0) orca.setLocusOfFocus(event, newFocus) def onNameChanged(self, event): '''Called whenever a property on an object changes. Arguments: - event: the Event ''' if event.source and event.source.getRole() == pyatspi.ROLE_DIALOG and event.source == orca_state.locusOfFocus: return None if self.pointOfReference.get('oldName', None) == event.source.name: return None self.pointOfReference['oldName'] = event.source.name orca.visualAppearanceChanged(event, event.source) def _speakContiguousSelection(self, obj, relationship): '''Check if the contiguous object has a selection. If it does, then speak it. If the user pressed Shift-Down, then look for an object with a RELATION_FLOWS_FROM relationship. If they pressed Shift-Up, then look for a RELATION_FLOWS_TO relationship. Arguments: - the current text object - the flows relationship (RELATION_FLOWS_FROM or RELATION_FLOWS_TO). Returns an indication of whether anything was spoken. ''' lastPos = self.pointOfReference.get('lastCursorPosition') if (self.isSameObject(lastPos[0], obj) or relationship == pyatspi.RELATION_FLOWS_TO) and lastPos[1] == 0: return False selSpoken = False current = obj for relation in current.getRelationSet(): if relation.getRelationType() == relationship: obj = relation.getTarget(0) objText = obj.queryText() if relationship == pyatspi.RELATION_FLOWS_FROM: start = lastPos[1] end = objText.characterCount else: start = 0 end = lastPos[1] if objText.getNSelections() > 0: (textContents, startOffset, endOffset) = self.whereAmI.getTextSelection(obj) if not start: pass startOffset = startOffset if not end: pass endOffset = endOffset self.sayPhrase(obj, startOffset, endOffset) selSpoken = True else: self.sayPhrase(obj, start, end) selSpoken = True objText.getNSelections() > 0 return selSpoken def _presentTextAtNewCaretPosition(self, event, otherObj = None): '''Updates braille, magnification, and outputs speech for the event.source or the otherObj.''' if not otherObj: pass obj = event.source text = obj.queryText() if obj: mag.magnifyAccessible(event, obj) brailleNeedsRepainting = True line = braille.getShowingLine() for region in line.regions: if isinstance(region, braille.Text) and region.accessible == obj: if region.repositionCursor(): braille.refresh(True) brailleNeedsRepainting = False break continue if brailleNeedsRepainting: self.updateBraille(obj) if not orca_state.lastInputEvent: return None if isinstance(orca_state.lastInputEvent, input_event.MouseButtonEvent): if not orca_state.lastInputEvent.pressed: self.sayLine(obj) return None if not isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent): return None keyString = orca_state.lastNonModifierKeyEvent.event_string mods = orca_state.lastInputEvent.modifiers isControlKey = mods & settings.CTRL_MODIFIER_MASK isShiftKey = mods & settings.SHIFT_MODIFIER_MASK lastPos = self.pointOfReference.get('lastCursorPosition') hasLastPos = lastPos != None if keyString == 'Up' or keyString == 'Down': if hasLastPos and isShiftKey and not isControlKey: if keyString == 'Up': self.sayPhrase(obj, startOffset, endOffset) selSpoken = self._speakContiguousSelection(obj, pyatspi.RELATION_FLOWS_TO) else: selSpoken = self._speakContiguousSelection(obj, pyatspi.RELATION_FLOWS_FROM) if startOffset != endOffset: self.sayPhrase(obj, startOffset, endOffset) else: (startOffset, endOffset) = self.getOffsetsForLine(obj) self.sayLine(obj) elif keyString == 'Left' or keyString == 'Right': if hasLastPos: pass inNewObj = not self.isSameObject(lastPos[0], obj) if hasLastPos and not inNewObj and isShiftKey and isControlKey: (startOffset, endOffset) = self.getOffsetsForPhrase(obj) self.sayPhrase(obj, startOffset, endOffset) elif isControlKey and not inNewObj: (startOffset, endOffset) = self.getOffsetsForWord(obj) if startOffset == endOffset: self.sayCharacter(obj) else: self.sayWord(obj) else: (startOffset, endOffset) = self.getOffsetsForChar(obj) self.sayCharacter(obj) elif keyString == 'Page_Up': if hasLastPos and isShiftKey and isControlKey: (startOffset, endOffset) = self.getOffsetsForPhrase(obj) self.sayPhrase(obj, startOffset, endOffset) elif isControlKey: (startOffset, endOffset) = self.getOffsetsForChar(obj) self.sayCharacter(obj) else: (startOffset, endOffset) = self.getOffsetsForLine(obj) self.sayLine(obj) elif keyString == 'Page_Down': if hasLastPos and isShiftKey and isControlKey: (startOffset, endOffset) = self.getOffsetsForPhrase(obj) self.sayPhrase(obj, startOffset, endOffset) else: (startOffset, endOffset) = self.getOffsetsForLine(obj) self.sayLine(obj) elif keyString == 'Home' or keyString == 'End': if hasLastPos and isShiftKey and not isControlKey: (startOffset, endOffset) = self.getOffsetsForPhrase(obj) self.sayPhrase(obj, startOffset, endOffset) elif isControlKey: (startOffset, endOffset) = self.getOffsetsForLine(obj) self.sayLine(obj) else: (startOffset, endOffset) = self.getOffsetsForChar(obj) self.sayCharacter(obj) else: startOffset = text.caretOffset endOffset = text.caretOffset self._saveLastCursorPosition(obj, text.caretOffset) self._saveSpokenTextRange(startOffset, endOffset) def onCaretMoved(self, event): '''Called whenever the caret moves. Arguments: - event: the Event ''' if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus: return None if self.flatReviewContext: self.toggleFlatReviewMode() self._presentTextAtNewCaretPosition(event) def onTextDeleted(self, event): '''Called whenever text is deleted from an object. Arguments: - event: the Event ''' if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus: return None if event.source.getRole() == pyatspi.ROLE_SLIDER: return None self.updateBraille(event.source) if not (orca_state.lastInputEvent) or not isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent): return None keyString = orca_state.lastNonModifierKeyEvent.event_string controlPressed = orca_state.lastInputEvent.modifiers & settings.CTRL_MODIFIER_MASK text = event.source.queryText() if keyString == 'BackSpace': character = event.any_data elif (keyString == 'Delete' or keyString == 'D') and controlPressed: offset = text.caretOffset (character, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR) else: return None if (event.source.getRole() == pyatspi.ROLE_SLIDER).getLinkIndex(event.source, text.caretOffset) >= 0: voice = self.voices[settings.HYPERLINK_VOICE] elif character.isupper(): voice = self.voices[settings.UPPERCASE_VOICE] else: voice = self.voices[settings.DEFAULT_VOICE] if len(character.decode('utf-8')) == 1: speech.speakCharacter(character, voice) else: speech.speak(character, voice, False) def onTextInserted(self, event): '''Called whenever text is inserted into an object. Arguments: - event: the Event ''' if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus: return None if event.source.getRole() == pyatspi.ROLE_SLIDER: return None self.updateBraille(event.source) text = event.any_data if event.source.getRole() == pyatspi.ROLE_SPIN_BUTTON: (spinValue, caretOffset, startOffset) = self.getTextLineAtCaret(event.source) speech.speak(spinValue) return None speakThis = False if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent): keyString = orca_state.lastNonModifierKeyEvent.event_string if event.source.getRole() == pyatspi.ROLE_TEXT: pass wasAutoComplete = event.source.queryText().getNSelections() wasCommand = orca_state.lastInputEvent.modifiers & settings.COMMAND_MODIFIER_MASK if text == ' ' or keyString == 'space' or text == keyString: pass elif wasCommand or wasAutoComplete: speakThis = True elif event.source.getRole() == pyatspi.ROLE_PASSWORD_TEXT and settings.enableKeyEcho and settings.enablePrintableKeys: text = '*' speakThis = True elif isinstance(orca_state.lastInputEvent, input_event.MouseButtonEvent) and orca_state.lastInputEvent.button == '2': speakThis = True if speakThis: if text.isupper(): speech.speak(text, self.voices[settings.UPPERCASE_VOICE]) else: speech.speak(text) try: text = event.source.queryText() except NotImplementedError: return None offset = min(event.detail1, text.caretOffset - 1) previousOffset = offset - 1 if offset < 0 or previousOffset < 0: return None (currentChar, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR) (previousChar, startOffset, endOffset) = text.getTextAtOffset(previousOffset, pyatspi.TEXT_BOUNDARY_CHAR) if settings.enableEchoBySentence and self.isSentenceDelimiter(currentChar, previousChar): self.echoPreviousSentence(event.source) elif settings.enableEchoByWord and self.isWordDelimiter(currentChar): self.echoPreviousWord(event.source) def onActiveDescendantChanged(self, event): '''Called when an object who manages its own descendants detects a change in one of its children. Arguments: - event: the Event ''' if not event.source.getState().contains(pyatspi.STATE_FOCUSED): return None child = event.any_data if child: speech.stop() orca.setLocusOfFocus(event, child) else: orca.setLocusOfFocus(event, event.source) if orca_state.locusOfFocus and orca_state.locusOfFocus != event.source: self.pointOfReference['activeDescendantInfo'] = [ orca_state.locusOfFocus.parent, orca_state.locusOfFocus.getIndexInParent()] def onLinkSelected(self, event): '''Called when a hyperlink is selected in a text area. Arguments: - event: the Event ''' orca.setLocusOfFocus(event, event.source) def onStateChanged(self, event): """Called whenever an object's state changes. Arguments: - event: the Event """ if event.type.startswith('object:state-changed:active'): if self.findCommandRun: self.findCommandRun = False self.find() return None if event.type.startswith('object:state-changed:selected') and orca_state.locusOfFocus: if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent): if orca_state.lastNonModifierKeyEvent: keyString = orca_state.lastNonModifierKeyEvent.event_string else: keyString = None mods = orca_state.lastInputEvent.modifiers isControlKey = mods & settings.CTRL_MODIFIER_MASK state = orca_state.locusOfFocus.getState() announceState = False if state.contains(pyatspi.STATE_FOCUSED) and self.isSameObject(event.source, orca_state.locusOfFocus): if keyString == 'space': if isControlKey: announceState = True else: eventState = event.source.getState() selected = eventState.contains(pyatspi.STATE_SELECTED) if selected: pass announceState = event.detail1 if (keyString == 'Down' or keyString == 'Up') and event.source.getRole() == pyatspi.ROLE_TABLE_CELL and state.contains(pyatspi.STATE_SELECTED): announceState = True if announceState: if event.detail1: speech.speak(C_('text', 'selected'), None, False) else: speech.speak(C_('text', 'unselected'), None, False) return None if event.type.startswith('object:state-changed:focused'): iconified = False try: window = self.getTopLevel(event.source) iconified = window.getState().contains(pyatspi.STATE_ICONIFIED) except: debug.println(debug.LEVEL_FINEST, 'onStateChanged: could not get frame of focused item') if not iconified: if event.detail1: self.onFocus(event) return None if event.source.getRole() == pyatspi.ROLE_TOOL_TIP: obj = event.source if event.type.startswith('object:state-changed:showing'): if event.detail1 == 1: self.presentTooltip(obj) elif orca_state.locusOfFocus and isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent) and orca_state.lastNonModifierKeyEvent.event_string == 'F1': self.updateBraille(orca_state.locusOfFocus) utterances = self.speechGenerator.getSpeech(orca_state.locusOfFocus, False) utterances.extend(self.tutorialGenerator.getTutorial(orca_state.locusOfFocus, False)) speech.speakUtterances(utterances) return None if event.source.getRole() in state_change_notifiers: notifiers = state_change_notifiers[event.source.getRole()] found = False for state in notifiers: if state and event.type.endswith(state): found = True break continue event.source.getRole() == pyatspi.ROLE_TOOL_TIP if found: orca.visualAppearanceChanged(event, event.source) def getOffsetsForPhrase(self, obj): '''Return the start and end offset for the given phrase Arguments: - obj: the Accessible object ''' text = obj.queryText() lastPos = self.pointOfReference.get('lastCursorPosition') startOffset = lastPos[1] endOffset = text.caretOffset if startOffset > endOffset or endOffset != -1 or startOffset == -1: temp = endOffset endOffset = startOffset startOffset = temp return [ startOffset, endOffset] def getOffsetsForLine(self, obj): '''Return the start and end offset for the given line Arguments: - obj: the Accessible object ''' (line, endOffset, startOffset) = self.getTextLineAtCaret(obj) return [ startOffset, endOffset] def getOffsetsForWord(self, obj): '''Return the start and end offset for the given word Arguments: - obj: the Accessible object ''' text = obj.queryText() offset = text.caretOffset (word, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_WORD_START) return [ startOffset, endOffset] def getOffsetsForChar(self, obj): '''Return the start and end offset for the given character Arguments: - obj: the Accessible object ''' text = obj.queryText() offset = text.caretOffset mods = orca_state.lastInputEvent.modifiers if mods & settings.SHIFT_MODIFIER_MASK and orca_state.lastNonModifierKeyEvent.event_string == 'Right': startOffset = offset - 1 endOffset = offset else: startOffset = offset endOffset = offset + 1 return [ startOffset, endOffset] def onTextSelectionChanged(self, event): """Called when an object's text selection changes. Arguments: - event: the Event """ obj = event.source if not self.pointOfReference.get('spokenTextRange'): pass spokenRange = [ 0, 0] (startOffset, endOffset) = spokenRange if not obj.getState().contains(pyatspi.STATE_FOCUSED): lastPos = self.pointOfReference.get('lastCursorPosition') if not lastPos: return None if endOffset - startOffset > 1: return None relationType = None for relation in lastPos[0].getRelationSet(): if relation.getRelationType() in [ pyatspi.RELATION_FLOWS_FROM, pyatspi.RELATION_FLOWS_TO] and self.isSameObject(obj, relation.getTarget(0)): relationType = relation.getRelationType() break continue endOffset - startOffset > 1 endOffset = 0 while obj and not endOffset: try: endOffset = obj.queryText().characterCount startOffset = max(0, endOffset - 1) except: lastPos if not endOffset: for relation in obj.getRelationSet(): if relation.getRelationType() == relationType: obj = relation.getTarget(0) break continue lastPos else: break self.speakTextSelectionState(obj, startOffset, endOffset) def onSelectionChanged(self, event): """Called when an object's selection changes. Arguments: - event: the Event """ if not event or not (event.source): return None if event.source.getRole() in (pyatspi.ROLE_COMBO_BOX, pyatspi.ROLE_MENU): self.lastSelectedMenu = event.source if event.source.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS): return None if event.source.getRole() == pyatspi.ROLE_COMBO_BOX: orca.visualAppearanceChanged(event, event.source) elif event.source != orca_state.locusOfFocus and event.source.getState().contains(pyatspi.STATE_FOCUSED): newFocus = event.source if event.source.childCount: selection = event.source.querySelection() if selection.nSelectedChildren > 0: newFocus = selection.getSelectedChild(0) orca.setLocusOfFocus(event, newFocus) def onValueChanged(self, event): """Called whenever an object's value changes. Currently, the value changes for non-focused objects are ignored. Arguments: - event: the Event """ if event.source.getRole() == pyatspi.ROLE_SPIN_BUTTON: return None value = event.source.queryValue() if 'oldValue' in self.pointOfReference and value.currentValue == self.pointOfReference['oldValue']: return None orca.visualAppearanceChanged(event, event.source) def onWindowActivated(self, event): '''Called whenever a toplevel window is activated. Arguments: - event: the Event ''' self.windowActivateTime = time.time() orca.setLocusOfFocus(event, event.source) orca_state.activeWindow = event.source def onWindowDeactivated(self, event): '''Called whenever a toplevel window is deactivated. Arguments: - event: the Event ''' if orca_state.locusOfFocus and orca_state.locusOfFocus.getApplication() == event.source.getApplication(): speech.stop() braille.clear() if self.flatReviewContext: self.drawOutline(-1, 0, 0, 0) self.flatReviewContext = None self.updateBraille(orca_state.locusOfFocus) if event.source == orca_state.activeWindow: orca.setLocusOfFocus(event, None) orca_state.activeWindow = None def onMouseButton(self, event): '''Called whenever the user presses or releases a mouse button. Arguments: - event: the Event ''' state = event.type[-1] if state == 'r': obj = orca_state.locusOfFocus try: text = obj.queryText() except: pass (textContents, startOffset, endOffset) = self.whereAmI.getTextSelections(obj, True) if textContents: utterances = [] utterances.append(textContents) utterances.append(C_('text', 'selected')) speech.speakUtterances(utterances) self.updateBraille(orca_state.locusOfFocus) def noOp(self, event): '''Just here to capture events. Arguments: - event: the Event ''' pass def isLayoutOnly(self, obj): '''Returns True if the given object is a table and is for layout purposes only.''' layoutOnly = False if obj: attributes = obj.getAttributes() else: attributes = None if obj and obj.getRole() == pyatspi.ROLE_TABLE and attributes: for attribute in attributes: if attribute == 'layout-guess:true': layoutOnly = True break continue elif obj and obj.getRole() == pyatspi.ROLE_PANEL: text = self.getDisplayedText(obj) label = self.getDisplayedLabel(obj) if not (label or len(label)) and text and len(text): layoutOnly = True if layoutOnly: debug.println(debug.LEVEL_FINEST, 'Object deemed to be for layout purposes only: %s' % obj) return layoutOnly def toggleTableCellReadMode(self, inputEvent = None): '''Toggles an indicator for whether we should just read the current table cell or read the whole row.''' settings.readTableCellRow = not (settings.readTableCellRow) if settings.readTableCellRow: line = _('Speak row') else: line = _('Speak cell') speech.speak(line) return True def getAtkNameForAttribute(self, attribName): '''Converts the given attribute name into the Atk equivalent. This is necessary because an application or toolkit (e.g. Gecko) might invent entirely new names for the same attributes. Arguments: - attribName: The name of the text attribute Returns the Atk equivalent name if found or attribName otherwise. ''' return self.attributeNamesDict.get(attribName, attribName) def getAppNameForAttribute(self, attribName): """Converts the given Atk attribute name into the application's equivalent. This is necessary because an application or toolkit (e.g. Gecko) might invent entirely new names for the same text attributes. Arguments: - attribName: The name of the text attribute Returns the application's equivalent name if found or attribName otherwise. """ for key, value in self.attributeNamesDict.items(): if value == attribName: return key return attribName def textAttrsToDictionary(self, tokenString): """Converts a string of text attribute tokens of the form <key>:<value>; into a dictionary of keys and values. Text before the colon is the key and text afterwards is the value. If there is a final semi-colon, then it's ignored. Arguments: - tokenString: the string of tokens containing <key>:<value>; pairs. Returns a list containing two items: A list of the keys in the order they were extracted from the text attribute string and a dictionary of key/value items. """ keyList = [] dictionary = { } allTokens = tokenString.split(';') for token in allTokens: item = token.split(':') if len(item) == 2: key = item[0].strip() attribute = item[1].strip() keyList.append(key) dictionary[key] = attribute continue return [ keyList, dictionary] def outputCharAttributes(self, keys, attributes): '''Speak each of the text attributes given dictionary. Arguments: - attributes: a dictionary of text attributes to speak. ''' for key in keys: localizedKey = text_attribute_names.getTextAttributeName(key) if key in attributes: line = '' attribute = attributes[key] localizedValue = text_attribute_names.getTextAttributeName(attribute) if attribute: key = self.getAtkNameForAttribute(key) if key == 'weight': if attribute == 'bold' or int(attribute) > 400: line = _('bold') elif key in ('left-margin', 'right-margin'): numericPoint = locale.localeconv()['decimal_point'] lastChar = attribute[len(attribute) - 1] if lastChar == numericPoint or lastChar in self.digits: line = ngettext('%s %s pixel', '%s %s pixels', int(attribute)) % (localizedKey, localizedValue) elif key in ('indent', 'size'): value = attribute.split('px') if len(value) > 1: line = ngettext('%s %s pixel', '%s %s pixels', float(value[0])) % (localizedKey, value[0]) elif key == 'family-name': localizedValue = attribute.split(',')[0].strip().strip('"') if not line: pass line = localizedKey + ' ' + localizedValue speech.speak(line) attribute def readCharAttributes(self, inputEvent = None): '''Reads the attributes associated with the current text character. Calls outCharAttributes to speak a list of attributes. By default, a certain set of attributes will be spoken. If this is not desired, then individual application scripts should override this method to only speak the subset required. ''' try: text = orca_state.locusOfFocus.queryText() except: pass caretOffset = text.caretOffset defAttributes = text.getDefaultAttributes() debug.println(debug.LEVEL_FINEST, 'readCharAttributes: default text attributes: %s' % defAttributes) (defUser, defDict) = self.textAttrsToDictionary(defAttributes) allAttributes = defDict charAttributes = text.getAttributes(caretOffset) if charAttributes[0]: (charList, charDict) = self.textAttrsToDictionary(charAttributes[0]) if allAttributes: for key in charDict.keys(): allAttributes[key] = charDict[key] else: allAttributes = charDict (userAttrList, userAttrDict) = self.textAttrsToDictionary(settings.enabledSpokenTextAttributes) attributes = { } for key in userAttrList: if key in allAttributes: textAttr = allAttributes.get(key) userAttr = userAttrDict.get(key) if textAttr != userAttr or len(userAttr) == 0: attributes[key] = textAttr len(userAttr) == 0 self.outputCharAttributes(userAttrList, attributes) if self.getLinkIndex(orca_state.locusOfFocus, caretOffset) >= 0: speech.speak(_('link')) return True def reportScriptInfo(self, inputEvent = None): '''Output useful information on the current script via speech and braille. This information will be helpful to script writers. ''' infoString = "SCRIPT INFO: Script name='%s'" % self.name app = orca_state.locusOfFocus.getApplication() if orca_state.locusOfFocus and app: infoString += " Application name='%s'" % app.name try: infoString += " Toolkit name='%s'" % app.toolkitName except: infoString += ' Toolkit unknown' try: infoString += " Version='%s'" % app.version except: infoString += ' Version unknown' debug.println(debug.LEVEL_INFO, infoString) speech.speak(infoString) braille.displayMessage(infoString) return True def bypassNextCommand(self, inputEvent = None): '''Causes the next keyboard command to be ignored by Orca and passed along to the current application. Returns True to indicate the input event has been consumed. ''' speech.speak(_('Bypass mode enabled.')) orca_state.bypassNextCommand = True return True def enterLearnMode(self, inputEvent = None): '''Turns learn mode on. The user must press the escape key to exit learn mode. Returns True to indicate the input event has been consumed. ''' if settings.learnModeEnabled: return True speech.speak(_('Entering learn mode. Press any key to hear its function. To exit learn mode, press the escape key.')) braille.displayMessage(_('Learn mode. Press escape to exit.')) settings.learnModeEnabled = True return True def pursueForFlatReview(self, obj): '''Determines if we should look any further at the object for flat review.''' try: state = obj.getState() except: debug.printException(debug.LEVEL_WARNING) return False return state.contains(pyatspi.STATE_SHOWING) def getShowingDescendants(self, parent): '''Given a parent that manages its descendants, return a list of Accessible children that are actually showing. This algorithm was inspired a little by the srw_elements_from_accessible logic in Gnopernicus, and makes the assumption that the children of an object that manages its descendants are arranged in a row and column format. Arguments: - parent: The accessible which manages its descendants Returns a list of Accessible descendants which are showing. ''' if not parent: return [] if not parent.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) or parent.childCount <= 50: return [] try: icomponent = parent.queryComponent() except NotImplementedError: parent.childCount <= 50 parent.childCount <= 50 parent return [] descendants = [] parentExtents = icomponent.getExtents(0) try: table = parent.queryTable() except NotImplementedError: parent.childCount <= 50 parent.childCount <= 50 parent table = None except: parent.childCount <= 50 gridSize = 7 currentY = parentExtents.y while currentY < parentExtents.y + parentExtents.height: currentX = parentExtents.x minHeight = sys.maxint while currentX < parentExtents.x + parentExtents.width: child = icomponent.getAccessibleAtPoint(currentX, currentY + 1, 0) if child: extents = child.queryComponent().getExtents(0) if extents.x >= 0 and extents.y >= 0: newX = extents.x + extents.width minHeight = min(minHeight, extents.height) if not descendants.count(child): descendants.append(child) else: newX = currentX + gridSize else: newX = currentX + gridSize if newX <= currentX: currentX += gridSize continue currentX = newX if minHeight == sys.maxint: minHeight = gridSize currentY += minHeight return descendants def visible(self, ax, ay, awidth, aheight, bx, by, bwidth, bheight): """Returns true if any portion of region 'a' is in region 'b' """ highestBottom = min(ay + aheight, by + bheight) lowestTop = max(ay, by) leftMostRightEdge = min(ax + awidth, bx + bwidth) rightMostLeftEdge = max(ax, bx) visible = False if lowestTop <= highestBottom and rightMostLeftEdge <= leftMostRightEdge: visible = True elif aheight == 0: if awidth == 0: if lowestTop == highestBottom: pass visible = leftMostRightEdge == rightMostLeftEdge else: visible = leftMostRightEdge <= rightMostLeftEdge elif awidth == 0: visible = lowestTop <= highestBottom return visible def getFlatReviewContext(self): '''Returns the flat review context, creating one if necessary.''' if not self.flatReviewContext: self.flatReviewContext = self.flatReviewContextClass(self) self.justEnteredFlatReviewMode = True self.targetCursorCell = braille.cursorCell return self.flatReviewContext def toggleFlatReviewMode(self, inputEvent = None): '''Toggles between flat review mode and focus tracking mode.''' if self.flatReviewContext: self.drawOutline(-1, 0, 0, 0) self.flatReviewContext = None self.updateBraille(orca_state.locusOfFocus) else: context = self.getFlatReviewContext() (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.WORD) self.drawOutline(x, y, width, height) self._reviewCurrentItem(inputEvent, self.targetCursorCell) return True def updateBrailleReview(self, targetCursorCell = 0): '''Obtains the braille regions for the current flat review line and displays them on the braille display. If the targetCursorCell is non-0, then an attempt will be made to postion the review cursor at that cell. Otherwise, we will pan in display-sized increments to show the review cursor.''' context = self.getFlatReviewContext() (regions, regionWithFocus) = context.getCurrentBrailleRegions() if not regions: regions = [] regionWithFocus = None line = braille.Line() line.addRegions(regions) braille.setLines([ line]) braille.setFocus(regionWithFocus, False) if regionWithFocus: braille.panToOffset(regionWithFocus.brailleOffset + regionWithFocus.cursorOffset) if self.justEnteredFlatReviewMode: braille.refresh(True, self.targetCursorCell) self.justEnteredFlatReviewMode = False else: braille.refresh(True, targetCursorCell) def _setFlatReviewContextToBeginningOfBrailleDisplay(self): '''Sets the character of interest to be the first character showing at the beginning of the braille display.''' context = self.getFlatReviewContext() (regions, regionWithFocus) = context.getCurrentBrailleRegions() for region in regions: if region.brailleOffset + len(region.string.decode('UTF-8')) > braille.viewport[0]: if isinstance(region, braille.ReviewText) or isinstance(region, braille.ReviewComponent): position = max(region.brailleOffset, braille.viewport[0]) offset = position - region.brailleOffset self.targetCursorCell = region.brailleOffset - braille.viewport[0] (word, charOffset) = region.zone.getWordAtOffset(offset) if word: self.flatReviewContext.setCurrent(word.zone.line.index, word.zone.index, word.index, charOffset) else: self.flatReviewContext.setCurrent(region.zone.line.index, region.zone.index, 0, 0) break continue def panBrailleLeft(self, inputEvent = None, panAmount = 0): '''Pans the braille display to the left. If panAmount is non-zero, the display is panned by that many cells. If it is 0, the display is panned one full display width. In flat review mode, panning beyond the beginning will take you to the end of the previous line. In focus tracking mode, the cursor stays at its logical position. In flat review mode, the review cursor moves to character associated with cell 0.''' if self.flatReviewContext: if braille.beginningIsShowing: self.flatReviewContext.goBegin(flat_review.Context.LINE) self.reviewPreviousCharacter(inputEvent) else: braille.panLeft(panAmount) self._setFlatReviewContextToBeginningOfBrailleDisplay() (charString, x, y, width, height) = self.flatReviewContext.getCurrent(flat_review.Context.CHAR) self.drawOutline(x, y, width, height) self.targetCursorCell = 1 self.updateBrailleReview(self.targetCursorCell) elif braille.beginningIsShowing and orca_state.locusOfFocus and self.isTextArea(orca_state.locusOfFocus): text = orca_state.locusOfFocus.queryText() (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START) movedCaret = False if startOffset > 0: movedCaret = text.setCaretOffset(startOffset - 1) if not movedCaret and orca_state.locusOfFocus.getRole() == pyatspi.ROLE_TERMINAL: context = self.getFlatReviewContext() context.goBegin(flat_review.Context.LINE) self.reviewPreviousCharacter(inputEvent) else: braille.panLeft(panAmount) braille.refresh(False) return True def panBrailleLeftOneChar(self, inputEvent = None): '''Nudges the braille display one character to the left. In focus tracking mode, the cursor stays at its logical position. In flat review mode, the review cursor moves to character associated with cell 0.''' self.panBrailleLeft(inputEvent, 1) def panBrailleRight(self, inputEvent = None, panAmount = 0): '''Pans the braille display to the right. If panAmount is non-zero, the display is panned by that many cells. If it is 0, the display is panned one full display width. In flat review mode, panning beyond the end will take you to the begininng of the next line. In focus tracking mode, the cursor stays at its logical position. In flat review mode, the review cursor moves to character associated with cell 0.''' if self.flatReviewContext: if braille.endIsShowing: self.flatReviewContext.goEnd(flat_review.Context.LINE) self.reviewNextCharacter(inputEvent) else: braille.panRight(panAmount) self._setFlatReviewContextToBeginningOfBrailleDisplay() (charString, x, y, width, height) = self.flatReviewContext.getCurrent(flat_review.Context.CHAR) self.drawOutline(x, y, width, height) self.targetCursorCell = 1 self.updateBrailleReview(self.targetCursorCell) elif braille.endIsShowing and orca_state.locusOfFocus and self.isTextArea(orca_state.locusOfFocus): text = orca_state.locusOfFocus.queryText() (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START) if endOffset < text.characterCount: text.setCaretOffset(endOffset) else: braille.panRight(panAmount) braille.refresh(False) return True def panBrailleRightOneChar(self, inputEvent = None): '''Nudges the braille display one character to the right. In focus tracking mode, the cursor stays at its logical position. In flat review mode, the review cursor moves to character associated with cell 0.''' self.panBrailleRight(inputEvent, 1) def goBrailleHome(self, inputEvent = None): '''Returns to the component with focus.''' if self.flatReviewContext: return self.toggleFlatReviewMode(inputEvent) return braille.returnToRegionWithFocus(inputEvent) def leftClickReviewItem(self, inputEvent = None): '''Performs a left mouse button click on the current item.''' self.getFlatReviewContext().clickCurrent(1) return True def rightClickReviewItem(self, inputEvent = None): '''Performs a right mouse button click on the current item.''' self.getFlatReviewContext().clickCurrent(3) return True def reviewCurrentLine(self, inputEvent): '''Brailles and speaks the current flat review line.''' self._reviewCurrentLine(inputEvent, 1) self.lastReviewCurrentEvent = inputEvent return True def reviewSpellCurrentLine(self, inputEvent): '''Brailles and spells the current flat review line.''' self._reviewCurrentLine(inputEvent, 2) self.lastReviewCurrentEvent = inputEvent return True def reviewPhoneticCurrentLine(self, inputEvent): '''Brailles and phonetically spells the current flat review line.''' self._reviewCurrentLine(inputEvent, 3) self.lastReviewCurrentEvent = inputEvent return True def _reviewCurrentLine(self, inputEvent, speechType = 1): '''Presents the current flat review line via braille and speech. Arguments: - inputEvent - the current input event. - speechType - the desired presentation: speak (1), spell (2), or phonetic (3) ''' context = self.getFlatReviewContext() (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE) self.drawOutline(x, y, width, height) if not isinstance(inputEvent, input_event.BrailleEvent): if not lineString and not len(lineString) or lineString == '\n': speech.speak(_('blank')) elif lineString.isspace(): speech.speak(_('white space')) elif lineString.isupper(): pass None if speechType < 2 or speechType > 3 else speechType > 3 if speechType == 2: self.spellCurrentItem(lineString) elif speechType == 3: self.phoneticSpellCurrentItem(lineString) else: lineString = self.adjustForRepeats(lineString) speech.speak(lineString) self.updateBrailleReview() return True def reviewPreviousLine(self, inputEvent): '''Moves the flat review context to the beginning of the previous line.''' context = self.getFlatReviewContext() moved = context.goPrevious(flat_review.Context.LINE, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentLine(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewHome(self, inputEvent): '''Moves the flat review context to the top left of the current window.''' context = self.getFlatReviewContext() context.goBegin() self._reviewCurrentLine(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewNextLine(self, inputEvent): '''Moves the flat review context to the beginning of the next line. Places the flat review cursor at the beginning of the line.''' context = self.getFlatReviewContext() moved = context.goNext(flat_review.Context.LINE, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentLine(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewBottomLeft(self, inputEvent): '''Moves the flat review context to the beginning of the last line in the window. Places the flat review cursor at the beginning of the line.''' context = self.getFlatReviewContext() context.goEnd(flat_review.Context.WINDOW) context.goBegin(flat_review.Context.LINE) self._reviewCurrentLine(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewEnd(self, inputEvent): '''Moves the flat review context to the end of the last line in the window. Places the flat review cursor at the end of the line.''' context = self.getFlatReviewContext() context.goEnd() self._reviewCurrentLine(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewCurrentItem(self, inputEvent, targetCursorCell = 0): '''Brailles and speaks the current item to the user.''' self._reviewCurrentItem(inputEvent, targetCursorCell, 1) self.lastReviewCurrentEvent = inputEvent return True def reviewSpellCurrentItem(self, inputEvent, targetCursorCell = 0): '''Brailles and spells the current item to the user.''' self._reviewCurrentItem(inputEvent, targetCursorCell, 2) self.lastReviewCurrentEvent = inputEvent return True def reviewPhoneticCurrentItem(self, inputEvent, targetCursorCell = 0): '''Brailles and phonetically spells the current item to the user.''' self._reviewCurrentItem(inputEvent, targetCursorCell, 3) self.lastReviewCurrentEvent = inputEvent return True def spellCurrentItem(self, itemString): '''Spell the current flat review word or line. Arguments: - itemString: the string to spell. ''' for charIndex, character in enumerate(itemString.decode('UTF-8')): if character.isupper(): speech.speak(character.encode('UTF-8'), self.voices[settings.UPPERCASE_VOICE]) continue speech.speak(character.encode('UTF-8')) def _reviewCurrentItem(self, inputEvent, targetCursorCell = 0, speechType = 1): '''Presents the current item to the user. Arguments: - inputEvent - the current input event. - targetCursorCell - if non-zero, the target braille cursor cell. - speechType - the desired presentation: speak (1), spell (2), or phonetic (3). ''' context = self.getFlatReviewContext() (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.WORD) self.drawOutline(x, y, width, height) if not isinstance(inputEvent, input_event.BrailleEvent): if not wordString and not len(wordString) or wordString == '\n': speech.speak(_('blank')) else: (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE) if lineString == '\n': speech.speak(_('blank')) elif wordString.isspace(): speech.speak(_('white space')) elif wordString.isupper() and speechType == 1: speech.speak(wordString, self.voices[settings.UPPERCASE_VOICE]) elif speechType == 2: self.spellCurrentItem(wordString) elif speechType == 3: self.phoneticSpellCurrentItem(wordString) elif speechType == 1: wordString = self.adjustForRepeats(wordString) speech.speak(wordString) self.updateBrailleReview(targetCursorCell) return True def reviewCurrentAccessible(self, inputEvent): context = self.getFlatReviewContext() (zoneString, x, y, width, height) = context.getCurrent(flat_review.Context.ZONE) self.drawOutline(x, y, width, height) if not isinstance(inputEvent, input_event.BrailleEvent): utterances = self.speechGenerator.getSpeech(context.getCurrentAccessible(), False) utterances.extend(self.tutorialGenerator.getTutorial(context.getCurrentAccessible(), False)) speech.speakUtterances(utterances) return True def reviewPreviousItem(self, inputEvent): '''Moves the flat review context to the previous item. Places the flat review cursor at the beginning of the item.''' context = self.getFlatReviewContext() moved = context.goPrevious(flat_review.Context.WORD, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentItem(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewNextItem(self, inputEvent): '''Moves the flat review context to the next item. Places the flat review cursor at the beginning of the item.''' context = self.getFlatReviewContext() moved = context.goNext(flat_review.Context.WORD, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentItem(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewCurrentCharacter(self, inputEvent): '''Brailles and speaks the current flat review character.''' self._reviewCurrentCharacter(inputEvent, 1) self.lastReviewCurrentEvent = inputEvent return True def reviewSpellCurrentCharacter(self, inputEvent): """Brailles and 'spells' (phonetically) the current flat review character. """ self._reviewCurrentCharacter(inputEvent, 2) self.lastReviewCurrentEvent = inputEvent return True def _reviewCurrentCharacter(self, inputEvent, speechType = 1): '''Presents the current flat review character via braille and speech. Arguments: - inputEvent - the current input event. - speechType - the desired presentation: speak (1) or phonetic (2) ''' context = self.getFlatReviewContext() (charString, x, y, width, height) = context.getCurrent(flat_review.Context.CHAR) self.drawOutline(x, y, width, height) if not isinstance(inputEvent, input_event.BrailleEvent): if not charString or not len(charString): speech.speak(_('blank')) else: (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE) if lineString == '\n': speech.speak(_('blank')) elif speechType == 2: self.phoneticSpellCurrentItem(charString) elif charString.decode('UTF-8').isupper(): speech.speakCharacter(charString, self.voices[settings.UPPERCASE_VOICE]) else: speech.speakCharacter(charString) self.updateBrailleReview() return True def reviewPreviousCharacter(self, inputEvent): '''Moves the flat review context to the previous character. Places the flat review cursor at character.''' context = self.getFlatReviewContext() moved = context.goPrevious(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentCharacter(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewEndOfLine(self, inputEvent): '''Moves the flat review context to the end of the line. Places the flat review cursor at the end of the line.''' context = self.getFlatReviewContext() context.goEnd(flat_review.Context.LINE) self.reviewCurrentCharacter(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewNextCharacter(self, inputEvent): '''Moves the flat review context to the next character. Places the flat review cursor at character.''' context = self.getFlatReviewContext() moved = context.goNext(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentCharacter(inputEvent) self.targetCursorCell = braille.cursorCell return True def reviewAbove(self, inputEvent): '''Moves the flat review context to the character most directly above the current flat review cursor. Places the flat review cursor at character.''' context = self.getFlatReviewContext() moved = context.goAbove(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentItem(inputEvent, self.targetCursorCell) return True def reviewBelow(self, inputEvent): '''Moves the flat review context to the character most directly below the current flat review cursor. Places the flat review cursor at character.''' context = self.getFlatReviewContext() moved = context.goBelow(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE) if moved: self._reviewCurrentItem(inputEvent, self.targetCursorCell) return True def showZones(self, inputEvent): '''Debug routine to paint rectangles around the discrete interesting (e.g., text) zones in the active window for this application. ''' flatReviewContext = self.getFlatReviewContext() lines = flatReviewContext.lines for line in lines: lineString = '' for zone in line.zones: lineString += " '%s' [%s]" % (zone.string, zone.accessible.getRoleName()) debug.println(debug.LEVEL_OFF, lineString) self.flatReviewContext = None def find(self, query = None): '''Searches for the specified query. If no query is specified, it searches for the query specified in the Orca Find dialog. Arguments: - query: The search query to find. ''' if not query: query = find.getLastQuery() if query: context = self.getFlatReviewContext() location = query.findQuery(context, self.justEnteredFlatReviewMode) if not location: message = _('string not found') braille.displayMessage(message) speech.speak(message) else: context.setCurrent(location.lineIndex, location.zoneIndex, location.wordIndex, location.charIndex) self.reviewCurrentItem(None) self.targetCursorCell = braille.cursorCell def findNext(self, inputEvent): '''Searches forward for the next instance of the string searched for via the Orca Find dialog. Other than direction and the starting point, the search options initially specified (case sensitivity, window wrap, and full/partial match) are preserved. ''' lastQuery = find.getLastQuery() if lastQuery: lastQuery.searchBackwards = False lastQuery.startAtTop = False self.find(lastQuery) else: orca.showFindGUI() def findPrevious(self, inputEvent): '''Searches backwards for the next instance of the string searched for via the Orca Find dialog. Other than direction and the starting point, the search options initially specified (case sensitivity, window wrap, and full/or partial match) are preserved. ''' lastQuery = find.getLastQuery() if lastQuery: lastQuery.searchBackwards = True lastQuery.startAtTop = False self.find(lastQuery) else: orca.showFindGUI() def goToBookmark(self, inputEvent): ''' Go to the bookmark indexed by inputEvent.hw_code. Delegates to Bookmark.goToBookmark ''' bookmarks = self.getBookmarks() bookmarks.goToBookmark(inputEvent) def addBookmark(self, inputEvent): ''' Add an in-page accessible object bookmark for this key. Delegates to Bookmark.addBookmark ''' bookmarks = self.getBookmarks() bookmarks.addBookmark(inputEvent) def bookmarkCurrentWhereAmI(self, inputEvent): ''' Report "Where am I" information for this bookmark relative to the current pointer location. Delegates to Bookmark.bookmarkCurrentWhereAmI''' bookmarks = self.getBookmarks() bookmarks.bookmarkCurrentWhereAmI(inputEvent) def saveBookmarks(self, inputEvent): ''' Save the bookmarks for this script. Delegates to Bookmark.saveBookmarks ''' bookmarks = self.getBookmarks() bookmarks.saveBookmarks(inputEvent) def goToNextBookmark(self, inputEvent): ''' Go to the next bookmark location. If no bookmark has yet to be selected, the first bookmark will be used. Delegates to Bookmark.goToNextBookmark ''' bookmarks = self.getBookmarks() bookmarks.goToNextBookmark(inputEvent) def goToPrevBookmark(self, inputEvent): ''' Go to the previous bookmark location. If no bookmark has yet to be selected, the first bookmark will be used. Delegates to Bookmark.goToPrevBookmark ''' bookmarks = self.getBookmarks() bookmarks.goToPrevBookmark(inputEvent) def _isInterestingObj(self, obj): import inspect interesting = False if getattr(obj, '__class__', None): name = obj.__class__.__name__ if name not in ('function', 'type', 'list', 'dict', 'tuple', 'wrapper_descriptor', 'module', 'method_descriptor', 'member_descriptor', 'instancemethod', 'builtin_function_or_method', 'frame', 'classmethod', 'classmethod_descriptor', '_Environ', 'MemoryError', '_Printer', '_Helper', 'getset_descriptor', 'weakref', 'property', 'cell', 'staticmethod', 'EventListener', 'KeystrokeListener', 'KeyBinding', 'InputEventHandler', 'Rolename'): try: filename = inspect.getabsfile(obj.__class__) if filename.index('orca'): interesting = True return interesting def _detectCycle(self, obj, visitedObjs, indent = ''): '''Attempts to discover a cycle in object references.''' import gc visitedObjs.append(obj) for referent in gc.get_referents(obj): try: if visitedObjs.index(referent): if self._isInterestingObj(referent): print indent, 'CYCLE!!!!', `referent` break except: pass self._detectCycle(referent, visitedObjs, ' ' + indent) visitedObjs.remove(obj) def _printObjInfo(self, indent, obj): '''Prints information about an object, if we care about it.''' if self._isInterestingObj(obj): print indent, obj.__class__.__name__, `obj` def printMemoryUsageHandler(self, inputEvent): '''Prints memory usage information.''' print 'TODO: print something useful for memory debugging' def printAppsHandler(self, inputEvent = None): '''Prints a list of all applications to stdout.''' self.printApps() return True def printActiveAppHandler(self, inputEvent = None): '''Prints the currently active application.''' self.printActiveApp() return True def printAncestryHandler(self, inputEvent = None): '''Prints the ancestry for the current locusOfFocus''' self.printAncestry(orca_state.locusOfFocus) return True def printHierarchyHandler(self, inputEvent = None): '''Prints the application for the current locusOfFocus''' if orca_state.locusOfFocus: self.printHierarchy(orca_state.locusOfFocus.getApplication(), orca_state.locusOfFocus) return True def isSameObject(self, obj1, obj2): if obj1 == obj2: return True if not obj1 or not obj2: return False try: if obj1.name != obj2.name or obj1.getRole() != obj2.getRole(): return False extents1 = obj1.queryComponent().getExtents(pyatspi.DESKTOP_COORDS) extents2 = obj2.queryComponent().getExtents(pyatspi.DESKTOP_COORDS) if extents1.x == extents2.x and extents1.y == extents2.y and extents1.width == extents2.width and extents1.height == extents2.height: return True parent1 = obj1 parent2 = obj2 while parent1 and parent2 and parent1.getState().contains(pyatspi.STATE_TRANSIENT) and parent2.getState().contains(pyatspi.STATE_TRANSIENT): if parent1.getIndexInParent() != parent2.getIndexInParent(): return False parent1 = parent1.parent parent2 = parent2.parent continue parent1.getIndexInParent() != parent2.getIndexInParent() if parent1 and parent2 and parent1 == parent2: return self.getRealActiveDescendant(obj1).name == self.getRealActiveDescendant(obj2).name except: not obj2 obj1 == obj2 try: parent1 = obj1 parent2 = obj2 while parent1 and parent2 and parent1.getRole() == pyatspi.ROLE_LABEL and parent2.getRole() == pyatspi.ROLE_LABEL: if parent1.getIndexInParent() != parent2.getIndexInParent(): return False parent1 = parent1.parent parent2 = parent2.parent continue parent1.getIndexInParent() != parent2.getIndexInParent() if parent1 and parent2 and parent1 == parent2: return True except: not obj2 obj1 == obj2 return False def appendString(self, text, newText, delimiter = ' '): '''Appends the newText to the given text with the delimiter in between and returns the new string. Edge cases, such as no initial text or no newText, are handled gracefully.''' if not newText or len(newText) == 0: return text if text and len(text): return text + delimiter + newText return newText def __hasLabelForRelation(self, label): '''Check if label has a LABEL_FOR relation Arguments: - label: the label in question Returns TRUE if label has a LABEL_FOR relation. ''' if not label or label.getRole() != pyatspi.ROLE_LABEL: return False relations = label.getRelationSet() for relation in relations: if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR: return True return False def __isLabeling(self, label, obj): '''Check if label is connected via LABEL_FOR relation with object Arguments: - obj: the object in question - labeled: the label in question Returns TRUE if label has a relation LABEL_FOR for object. ''' if not obj and not label or label.getRole() != pyatspi.ROLE_LABEL: return False relations = label.getRelationSet() if not relations: return False for relation in relations: if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR: for i in range(0, relation.getNTargets()): target = relation.getTarget(i) if target == obj: return True target == obj return False def getUnicodeCurrencySymbols(self): '''Return a list of the unicode currency symbols, populating the list if this is the first time that this routine has been called. Returns a list of unicode currency symbols. ''' if not self._unicodeCurrencySymbols: self._unicodeCurrencySymbols = [ u'$', u'¬¢', u'¬£', u'¬§', u'¬•', u'Δí', u'ÿã', u'‡ß≤', u'‡ß≥', u'‡´±', u'‡Øπ', u'‡∏ø', u'·üõ', u'‚Ñ≥', u'ÂÖÉ', u'ÂÜÜ', u'ÂúÜ', u'Âúì', u'Ô∑º'] for ordChar in range(ord(u'‚dž'), ord(u'‚ǵ') + 1): self._unicodeCurrencySymbols.append(unichr(ordChar)) return self._unicodeCurrencySymbols def findDisplayedLabel(self, obj): '''Return a list of the objects that are labelling this object. Argument: - obj: the object in question Returns a list of the objects that are labelling this object. ''' label = [] relations = obj.getRelationSet() allTargets = [] for relation in relations: if relation.getRelationType() == pyatspi.RELATION_LABELLED_BY: for i in range(0, relation.getNTargets()): target = relation.getTarget(i) if target not in allTargets: allTargets.append(target) label.append(target) continue if not len(label): potentialLabels = [] useLabel = False if obj.getRole() == pyatspi.ROLE_EMBEDDED: candidate = obj while candidate.childCount: candidate = candidate[0] candidate = candidate.parent for child in candidate: if child.getRole() == pyatspi.ROLE_FILLER: candidate = child break continue for child in candidate: if child.getRole() == pyatspi.ROLE_LABEL: useLabel = True potentialLabels.append(child) continue elif (obj.getRole() == pyatspi.ROLE_FILLER or obj.getRole() == pyatspi.ROLE_PANEL) and obj.childCount == 2: (child0, child1) = obj child0_role = child0.getRole() child1_role = child1.getRole() if child0_role == pyatspi.ROLE_LABEL and not self._Script__hasLabelForRelation(child0) and child1_role in [ pyatspi.ROLE_FILLER, pyatspi.ROLE_PANEL]: useLabel = True potentialLabels.append(child0) elif child1_role == pyatspi.ROLE_LABEL and not self._Script__hasLabelForRelation(child1) and child0_role in [ pyatspi.ROLE_FILLER, pyatspi.ROLE_PANEL]: useLabel = True potentialLabels.append(child1) else: parent = obj.parent if parent: if parent.getRole() == pyatspi.ROLE_FILLER or parent.getRole() == pyatspi.ROLE_PANEL: for potentialLabel in parent: try: useLabel = self._Script__isLabeling(potentialLabel, obj) if useLabel: potentialLabels.append(potentialLabel) break continue continue if useLabel and len(potentialLabels): label = potentialLabels return label def getDisplayedLabel(self, obj): '''If there is an object labelling the given object, return the text being displayed for the object labelling this object. Otherwise, return None. Argument: - obj: the object in question Returns the string of the object labelling this object, or None if there is nothing of interest here. ''' labelString = None labels = self.findDisplayedLabel(obj) for label in labels: labelString = self.appendString(labelString, self.getDisplayedText(label)) return labelString def __getDisplayedTextInComboBox(self, combo): '''Returns the text being displayed in a combo box. If nothing is displayed, then None is returned. Arguments: - combo: the combo box Returns the text in the combo box or an empty string if nothing is displayed. ''' displayedText = None textObj = None for child in combo: if child and child.getRole() == pyatspi.ROLE_TEXT: textObj = child continue if textObj: (displayedText, caretOffset, startOffset) = self.getTextLineAtCaret(textObj) else: try: comboSelection = combo.querySelection() selectedItem = comboSelection.getSelectedChild(0) except: selectedItem = None if selectedItem: displayedText = self.getDisplayedText(selectedItem) elif combo.name and len(combo.name): displayedText = combo.name else: (displayedText, caretOffset, startOffset) = self.getTextLineAtCaret(combo) if not displayedText: pass displayedText = None return displayedText def getDisplayedText(self, obj): """Returns the text being displayed for an object. Arguments: - obj: the object Returns the text being displayed for an object or None if there isn't any text being shown. """ displayedText = None role = obj.getRole() if role == pyatspi.ROLE_COMBO_BOX: return self._Script__getDisplayedTextInComboBox(obj) try: text = obj.queryText() except NotImplementedError: role == pyatspi.ROLE_COMBO_BOX role == pyatspi.ROLE_COMBO_BOX except: role == pyatspi.ROLE_COMBO_BOX displayedText = text.getText(0, -1) unicodeText = displayedText.decode('UTF-8') if unicodeText and len(unicodeText) == 1 and unicodeText[0] == self.EMBEDDED_OBJECT_CHARACTER and obj.childCount > 0: try: displayedText = self.getDisplayedText(obj[0]) debug.printException(debug.LEVEL_WARNING) elif unicodeText: for i in range(0, len(unicodeText)): if unicodeText[i] == self.EMBEDDED_OBJECT_CHARACTER: displayedText = None break continue role == pyatspi.ROLE_COMBO_BOX if not displayedText: displayedText = obj.name if not displayedText and role == pyatspi.ROLE_PUSH_BUTTON: for child in obj: if child.getRole() == pyatspi.ROLE_LABEL: childText = self.getDisplayedText(child) if childText and len(childText): displayedText = self.appendString(displayedText, childText) len(childText) return displayedText def getTextForValue(self, obj): """Returns the text to be displayed for the object's current value. Arguments: - obj: the Accessible object that may or may not have a value. Returns a string representing the value. """ attributes = obj.getAttributes() for attribute in attributes: if attribute.startswith('valuetext'): return attribute[10:] try: value = obj.queryValue() except NotImplementedError: attribute.startswith('valuetext') attribute.startswith('valuetext') return '' try: minimumIncrement = value.minimumIncrement except: attribute.startswith('valuetext') minimumIncrement = 0 if minimumIncrement == 0: minimumIncrement = (value.maximumValue - value.minimumValue) / 100 try: decimalPlaces = max(0, -math.log10(minimumIncrement)) except: try: decimalPlaces = max(0, -math.log10(value.minimumValue)) try: decimalPlaces = max(0, -math.log10(value.maximumValue)) decimalPlaces = 0 formatter = '%%.%df' % decimalPlaces valueString = formatter % value.currentValue return valueString def findFocusedObject(self, root): '''Returns the accessible that has focus under or including the given root. TODO: This will currently traverse all children, whether they are visible or not and/or whether they are children of parents that manage their descendants. At some point, this method should be optimized to take such things into account. Arguments: - root: the root object where to start searching Returns the object with the FOCUSED state or None if no object with the FOCUSED state can be found. ''' if root.getState().contains(pyatspi.STATE_FOCUSED): return root for child in root: try: candidate = self.findFocusedObject(child) if candidate: return candidate continue continue def getRealActiveDescendant(self, obj): '''Given an object that should be a child of an object that manages its descendants, return the child that is the real active descendant carrying useful information. Arguments: - obj: an object that should be a child of an object that manages its descendants. ''' if obj.getRole() == pyatspi.ROLE_TABLE_CELL and obj.childCount: nonTableCellFound = False for child in obj: if child.getRole() != pyatspi.ROLE_TABLE_CELL: nonTableCellFound = True continue if not nonTableCellFound: for child in obj: try: text = child.queryText() except NotImplementedError: continue continue if text.getText(0, -1): return child if obj and obj.childCount: return obj[-1] return obj def isDesiredFocusedItem(self, obj, rolesList): """Called to determine if the given object and it's hierarchy of parent objects, each have the desired roles. Arguments: - obj: the accessible object to check. - rolesList: the list of desired roles for the components and the hierarchy of its parents. Returns True if all roles match. """ current = obj for role in rolesList: if current is None: return False if not isinstance(role, list): role = [ role] if isinstance(role[0], str): current_role = current.getRoleName() else: current_role = current.getRole() if current_role not in role: return False current = current.parent return True def speakMisspeltWord(self, allTokens, badWord): '''Called by various spell checking routine to speak the misspelt word, plus the context that it is being used in. Arguments: - allTokens: a list of all the words. - badWord: the misspelt word. ''' for i in range(0, len(allTokens)): if allTokens[i].startswith(badWord): minIndex = i - 5 if minIndex < 0: minIndex = 0 maxIndex = i + 5 if maxIndex > len(allTokens) - 1: maxIndex = len(allTokens) - 1 utterances = [ _('Misspelled word: %s') % badWord] contextPhrase = ' '.join(allTokens[minIndex:maxIndex + 1]) utterances.append(_('Context is %s') % contextPhrase) text = ' '.join(utterances) speech.speak(text) continue def textLines(self, obj): '''Creates a generator that can be used to iterate over each line of a text object, starting at the caret offset. Arguments: - obj: an Accessible that has a text specialization Returns an iterator that produces elements of the form: [SayAllContext, acss], where SayAllContext has the text to be spoken and acss is an ACSS instance for speaking the text. ''' try: text = obj.queryText() except: return None length = text.characterCount offset = text.caretOffset if settings.sayAllStyle == settings.SAYALL_STYLE_SENTENCE: mode = pyatspi.TEXT_BOUNDARY_SENTENCE_END elif settings.sayAllStyle == settings.SAYALL_STYLE_LINE: mode = pyatspi.TEXT_BOUNDARY_LINE_START else: mode = pyatspi.TEXT_BOUNDARY_LINE_START done = False while not done: lastEndOffset = -1 while offset < length: (lineString, startOffset, endOffset) = text.getTextAtOffset(offset, mode) if not lineString: mode = pyatspi.TEXT_BOUNDARY_LINE_START (lineString, startOffset, endOffset) = text.getTextAtOffset(offset, mode) if startOffset < 0: break if endOffset == lastEndOffset: offset = max(offset + 1, lastEndOffset + 1) lastEndOffset = endOffset continue lastEndOffset = endOffset offset = endOffset lineString = self.adjustForRepeats(lineString) if lineString.isupper(): voice = settings.voices[settings.UPPERCASE_VOICE] else: voice = settings.voices[settings.DEFAULT_VOICE] yield [ speechserver.SayAllContext(obj, lineString, startOffset, endOffset), voice] moreLines = False relations = obj.getRelationSet() for relation in relations: if relation.getRelationType() == pyatspi.RELATION_FLOWS_TO: obj = relation.getTarget(0) try: text = obj.queryText() except NotImplementedError: return None length = text.characterCount offset = 0 moreLines = True break continue if not moreLines: done = True continue def _addRepeatSegment(self, segment, line, respectPunctuation = True): '''Add in the latest line segment, adjusting for repeat characters and punctuation. Arguments: - segment: the segment of repeated characters. - line: the current built-up line to characters to speak. - respectPunctuation: if False, ignore punctuation level. Returns: the current built-up line plus the new segment, after adjusting for repeat character counts and punctuation. ''' style = settings.verbalizePunctuationStyle isPunctChar = True try: (level, action) = punctuation_settings.getPunctuationInfo(segment[0]) except: isPunctChar = False count = len(segment) if count >= settings.repeatCharacterLimit and segment[0] not in self.whitespace: if (not respectPunctuation or isPunctChar) and style <= level: repeatChar = chnames.getCharacterName(segment[0]) line += ' ' + ngettext('%(count)d %(repeatChar)s character', '%(count)d %(repeatChar)s characters', count) % { 'count': count, 'repeatChar': repeatChar } else: line += segment else: line += segment return line def adjustForLinks(self, obj, line, startOffset): '''Adjust line to include the word "link" after any hypertext links. Arguments: - obj: the accessible object that this line came from. - line: the string to adjust for links. - startOffset: the caret offset at the start of the line. Returns: a new line adjusted to add the speaking of "link" after text which is also a link. ''' line = line.decode('UTF-8') endOffset = startOffset + len(line) try: hyperText = obj.queryHypertext() nLinks = hyperText.getNLinks() except: nLinks = 0 adjustedLine = list(line) for n in range(nLinks, 0, -1): link = hyperText.getLink(n - 1) if link.endIndex < link.endIndex: pass elif link.endIndex < endOffset: index = link.endIndex - startOffset elif link.startIndex <= link.startIndex: pass elif link.startIndex < endOffset: index = len(line) - 1 linkString = ' ' + _('link') nextChar = adjustedLine[index] if not nextChar in self.whitespace or punctuation_settings.getPunctuationInfo(nextChar): linkString += ' ' adjustedLine[index:index] = linkString return ''.join(adjustedLine).encode('UTF-8') def adjustForRepeats(self, line): """Adjust line to include repeat character counts. As some people will want this and others might not, there is a setting in settings.py that determines whether this functionality is enabled. repeatCharacterLimit = <n> If <n> is 0, then there would be no repeat characters. Otherwise <n> would be the number of same characters (or more) in a row that cause the repeat character count output. If the value is set to 1, 2 or 3 then it's treated as if it was zero. In other words, no repeat character count is given. Arguments: - line: the string to adjust for repeat character counts. Returns: a new line adjusted for repeat character counts (if enabled). """ line = line.decode('UTF-8') if len(line) < 4 or settings.repeatCharacterLimit < 4: return line.encode('UTF-8') newLine = u'' multipleChars = False for i in range(1, len(line)): lastChar = line[i] newLine = self._addRepeatSegment(segment, newLine, multipleChars) return newLine.encode('UTF-8') def _getPronunciationForSegment(self, segment): '''Adjust the word segment to potentially replace it with what those words actually sound like. Two pronunciation dictionaries are checked. First the application specific one (which might be empty), then the default (global) one. Arguments: - segment: the string to adjust for words in the pronunciation dictionaries. Returns: a new word segment adjusted for words found in the pronunciation dictionaries, or the original word segment if there was no dictionary entry. ''' newSegment = pronunciation_dict.getPronunciation(segment, self.app_pronunciation_dict) if newSegment == segment: newSegment = pronunciation_dict.getPronunciation(segment) return newSegment def adjustForPronunciation(self, line): '''Adjust the line to replace words in the pronunciation dictionary, with what those words actually sound like. Arguments: - line: the string to adjust for words in the pronunciation dictionary. Returns: a new line adjusted for words found in the pronunciation dictionary. ''' line = line.decode('UTF-8') newLine = segment = u'' for i in range(0, len(line)): if self.isWordDelimiter(line[i]): if len(segment) != 0: newLine = newLine + self._getPronunciationForSegment(segment) newLine = newLine + line[i] segment = u'' continue segment += line[i] if len(segment) != 0: newLine = newLine + self._getPronunciationForSegment(segment) return newLine.encode('UTF-8') def getLinkIndex(self, obj, characterIndex): '''A brute force method to see if an offset is a link. This is provided because not all Accessible Hypertext implementations properly support the getLinkIndex method. Returns an index of 0 or greater of the characterIndex is on a hyperlink. Arguments: -obj: the Accessible object with the Accessible Hypertext specialization -characterIndex: the text position to check ''' if not obj: return -1 try: obj.queryText() except NotImplementedError: obj obj return -1 try: hypertext = obj.queryHypertext() except NotImplementedError: obj obj return -1 for i in xrange(hypertext.getNLinks()): link = hypertext.getLink(i) if characterIndex >= link.startIndex and characterIndex <= link.endIndex: return i return -1 def getCellIndex(self, obj): '''Returns the index of the cell which should be used with the table interface. This is necessary because in some apps we cannot count on getIndexInParent() returning the index we need. Arguments: -obj: the table cell whose index we need. ''' return obj.getIndexInParent() def isSentenceDelimiter(self, currentChar, previousChar): '''Returns True if we are positioned at the end of a sentence. This is determined by checking if the current character is a white space character and the previous character is one of the normal end-of-sentence punctuation characters. Arguments: - currentChar: the current character - previousChar: the previous character Returns True if the given character is a sentence delimiter. ''' if not isinstance(currentChar, unicode): currentChar = currentChar.decode('UTF-8') if not isinstance(previousChar, unicode): previousChar = previousChar.decode('UTF-8') if currentChar == '\r' or currentChar == '\n': return True if currentChar in self.whitespace: pass return previousChar in '!.?:;' def isWordDelimiter(self, character): '''Returns True if the given character is a word delimiter. Arguments: - character: the character in question Returns True if the given character is a word delimiter. ''' if not isinstance(character, unicode): character = character.decode('UTF-8') if not character in self.whitespace and character in '!*+,-./:;<=>?@[\\]^_{|}': pass return character == self.NO_BREAK_SPACE_CHARACTER def getFrame(self, obj): '''Returns the frame containing this object, or None if this object is not inside a frame. Arguments: - obj: the Accessible object ''' if not 'Finding frame for source.name=' + obj.name: pass debug.println(debug.LEVEL_FINEST, 'None') while obj and obj != obj.parent and obj.getRole() != pyatspi.ROLE_FRAME: obj = obj.parent if obj: if not '--> obj.name=' + obj.name: pass debug.println(debug.LEVEL_FINEST, 'None') continue if obj and obj.getRole() == pyatspi.ROLE_FRAME: pass else: obj = None return obj def getTopLevel(self, obj): '''Returns the top-level object (frame, dialog ...) containing this object, or None if this object is not inside a top-level object. Arguments: - obj: the Accessible object ''' if not 'Finding top-level object for source.name=' + obj.name: pass debug.println(debug.LEVEL_FINEST, 'None') while obj and obj.parent and obj != obj.parent and obj.parent.getRole() != pyatspi.ROLE_APPLICATION: obj = obj.parent if not '--> obj.name=' + obj.name: pass debug.println(debug.LEVEL_FINEST, 'None') if obj and obj.parent and obj.parent.getRole() == pyatspi.ROLE_APPLICATION: pass else: obj = None return obj def getTopLevelName(self, obj): ''' Returns the name of the top-level object. See getTopLevel. ''' top = self.getTopLevel(obj) if not top or not (top.name): return '' return top.name def getTextLineAtCaret(self, obj, offset = None): '''Gets the line of text where the caret is. Argument: - obj: an Accessible object that implements the AccessibleText interface - offset: an optional caret offset to use. (Not used here at the moment, but needed in the Gecko script) Returns the [string, caretOffset, startOffset] for the line of text where the caret is. ''' try: text = obj.queryText() except NotImplementedError: return [ '', 0, 0] if text.caretOffset == text.characterCount: caretOffset = max(0, text.caretOffset - 1) character = text.getText(caretOffset, caretOffset + 1).decode('UTF-8') else: caretOffset = text.caretOffset character = None if text.caretOffset == text.characterCount and character == '\n': content = '' startOffset = caretOffset elif text.characterCount == 1: lineString = text.getText(caretOffset, caretOffset + 1) startOffset = caretOffset else: (lineString, startOffset, endOffset) = text.getTextAtOffset(caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START) content = lineString.decode('UTF-8') if content[-1:] == '\n': content = content[:-1] return [ content.encode('UTF-8'), text.caretOffset, startOffset] def getNodeLevel(self, obj): '''Determines the node level of this object if it is in a tree relation, with 0 being the top level node. If this object is not in a tree relation, then -1 will be returned. Arguments: -obj: the Accessible object ''' if not obj: return -1 nodes = [] node = obj done = False while not done: relations = node.getRelationSet() node = None for relation in relations: if relation.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF: node = relation.getTarget(0) break continue obj if len(nodes) > 100 or nodes.count(node): debug.println(debug.LEVEL_WARNING, 'Script.getNodeLevel detected a cycle!!!') done = True continue if node: nodes.append(node) debug.println(debug.LEVEL_FINEST, 'Script.getNodeLevel %d' % len(nodes)) continue done = True return len(nodes) - 1 def getChildNodes(self, obj): '''Gets all of the children that have RELATION_NODE_CHILD_OF pointing to this expanded table cell. Arguments: -obj: the Accessible Object Returns: a list of all the child nodes ''' try: table = obj.parent.queryTable() except: return [] if not obj.getState().contains(pyatspi.STATE_EXPANDED): return [] nodes = [] index = self.getCellIndex(obj) row = table.getRowAtIndex(index) col = table.getColumnAtIndex(index) nodeLevel = self.getNodeLevel(obj) done = False for i in range(row + 1, table.nRows): cell = table.getAccessibleAt(i, col) relations = cell.getRelationSet() for relation in relations: if relation.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF: nodeOf = relation.getTarget(0) if self.isSameObject(obj, nodeOf): nodes.append(cell) else: currentLevel = self.getNodeLevel(nodeOf) if currentLevel <= nodeLevel: done = True break continue if done: break continue return nodes def getKeyBinding(self, obj): '''Gets the mnemonic, accelerator string and possibly shortcut for the given object. These are based upon the first accessible action for the object. Arguments: - obj: the Accessible object Returns: list containing strings: [mnemonic, shortcut, accelerator] ''' try: action = obj.queryAction() except NotImplementedError: return [ '', '', ''] bindingStrings = action.getKeyBinding(0).decode('UTF-8').split(';') if len(bindingStrings) == 3: mnemonic = bindingStrings[0] fullShortcut = bindingStrings[1] accelerator = bindingStrings[2] elif len(bindingStrings) > 0: mnemonic = '' fullShortcut = bindingStrings[0] try: accelerator = bindingStrings[1] accelerator = '' else: mnemonic = '' fullShortcut = '' accelerator = '' fullShortcut = fullShortcut.replace('<', '') fullShortcut = fullShortcut.replace('>', ' ') fullShortcut = fullShortcut.replace(':', ' ').strip() if mnemonic.endswith(' '): mnemonic += _('space') mnemonic = mnemonic.replace('<', '') mnemonic = mnemonic.replace('>', ' ').strip() if accelerator.endswith(' '): accelerator += _('space') accelerator = accelerator.replace('<', '') accelerator = accelerator.replace('>', ' ').strip() debug.println(debug.LEVEL_FINEST, 'default.getKeyBinding: ' + repr([ mnemonic, fullShortcut, accelerator])) return [ mnemonic, fullShortcut, accelerator] def getKnownApplications(self): '''Retrieves the list of currently running apps for the desktop as a list of Accessible objects. ''' debug.println(debug.LEVEL_FINEST, 'Script.getKnownApplications...') apps = filter((lambda x: x is not None), pyatspi.Registry.getDesktop(0)) debug.println(debug.LEVEL_FINEST, '...Script.getKnownApplications') return apps def getObjects(self, root, onlyShowing = True): '''Returns a list of all objects under the given root. Objects are returned in no particular order - this function does a simple tree traversal, ignoring the children of objects which report the MANAGES_DESCENDANTS state. Arguments: - root: the Accessible object to traverse - onlyShowing: examine only those objects that are SHOWING Returns: a list of all objects under the specified object ''' objlist = [] if root.childCount <= 0: return objlist for i, child in enumerate(root): debug.println(debug.LEVEL_FINEST, 'Script.getObjects looking at child %d' % i) if child: if (not onlyShowing or onlyShowing) and child.getState().contains(pyatspi.STATE_SHOWING): objlist.append(child) if child.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) == 0 and child.childCount > 0: objlist.extend(self.getObjects(child, onlyShowing)) child.childCount > 0 return objlist def findByRole(self, root, role, onlyShowing = True): """Returns a list of all objects of a specific role beneath the given root. [[[TODO: MM - This is very inefficient - this should do it's own traversal and not add objects to the list that aren't of the specified role. Instead it uses the traversal from getObjects and then deletes objects from the list that aren't of the specified role. Logged as bugzilla bug 319740.]]] Arguments: - root the Accessible object to traverse - role the string describing the Accessible role of the object - onlyShowing: examine only those objects that are SHOWING Returns a list of descendants of the root that are of the given role. """ objlist = [] allobjs = self.getObjects(root, onlyShowing) for o in allobjs: if o.getRole() == role: objlist.append(o) continue return objlist def findUnrelatedLabels(self, root): '''Returns a list containing all the unrelated (i.e., have no relations to anything and are not a fundamental element of a more atomic component like a combo box) labels under the given root. Note that the labels must also be showing on the display. Arguments: - root the Accessible object to traverse Returns a list of unrelated labels under the given root. ''' allLabels = self.findByRole(root, pyatspi.ROLE_LABEL) unrelatedLabels = [] for label in allLabels: relations = label.getRelationSet() if len(relations) == 0: parent = label.parent if parent and parent.getRole() == pyatspi.ROLE_PUSH_BUTTON: pass elif parent and parent.getRole() == pyatspi.ROLE_PANEL and parent.name == label.name: pass elif label.getState().contains(pyatspi.STATE_SHOWING): unrelatedLabels.append(label) parent.name == label.name sortedLabels = [] for label in unrelatedLabels: label_extents = label.queryComponent().getExtents(pyatspi.DESKTOP_COORDS) index = 0 for sortedLabel in sortedLabels: sorted_extents = sortedLabel.queryComponent().getExtents(pyatspi.DESKTOP_COORDS) if (label_extents.y > sorted_extents.y or label_extents.y == sorted_extents.y) and label_extents.x > sorted_extents.x: index += 1 continue sortedLabels.insert(index, label) return sortedLabels def phoneticSpellCurrentItem(self, itemString): '''Phonetically spell the current flat review word or line. Arguments: - itemString: the string to phonetically spell. ''' for charIndex, character in enumerate(itemString.decode('UTF-8')): if character.isupper(): voice = settings.voices[settings.UPPERCASE_VOICE] character = character.lower() else: voice = settings.voices[settings.DEFAULT_VOICE] phoneticString = phonnames.getPhoneticName(character) speech.speak(phoneticString, voice) def printAncestry(self, child): """Prints a hierarchical view of a child's ancestry.""" if not child: return None ancestorList = [ child] parent = child.parent while parent and parent.parent != parent: ancestorList.insert(0, parent) parent = parent.parent continue child indent = '' for ancestor in ancestorList: print indent + '+-', debug.getAccessibleDetails(ancestor) indent += ' ' def printHierarchy(self, root, ooi, indent = '', onlyShowing = True, omitManaged = True): '''Prints the accessible hierarchy of all children Arguments: -indent: Indentation string -root: Accessible where to start -ooi: Accessible object of interest -onlyShowing: If True, only show children painted on the screen -omitManaged: If True, omit children that are managed descendants ''' if not root: return None if root == ooi: print indent + '(*)', debug.getAccessibleDetails(root) else: print indent + '+-', debug.getAccessibleDetails(root) rootManagesDescendants = root.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) for child in root: if child == root: print indent + ' ' + 'WARNING CHILD == PARENT!!!' continue if not child: print indent + ' ' + 'WARNING CHILD IS NONE!!!' continue if child.parent != root: print indent + ' ' + 'WARNING CHILD.PARENT != PARENT!!!' continue if not onlyShowing and onlyShowing: pass paint = child.getState().contains(pyatspi.STATE_SHOWING) if paint and not omitManaged and omitManaged: pass paint = not rootManagesDescendants if paint: self.printHierarchy(child, ooi, indent + ' ', onlyShowing, omitManaged) continue def printApps(self): '''Prints a list of all applications to stdout.''' level = debug.LEVEL_OFF apps = self.getKnownApplications() debug.println(level, 'There are %d Accessible applications' % len(apps)) for app in apps: debug.printDetails(level, ' App: ', app, False) for child in app: debug.printDetails(level, ' Window: ', child, False) if child.parent != app: debug.println(level, " WARNING: child's parent is not app!!!") continue return True def printActiveApp(self): '''Prints the active application.''' level = debug.LEVEL_OFF window = self.findActiveWindow() if not window: debug.println(level, 'Active application: None') else: app = window.getApplication() if not app: debug.println(level, 'Active application: None') else: debug.println(level, 'Active application: %s' % app.name) def isInActiveApp(self, obj): '''Returns True if the given object is from the same application that currently has keyboard focus. Arguments: - obj: an Accessible object ''' if not obj: return False if orca_state.locusOfFocus: pass return orca_state.locusOfFocus.getApplication() == obj.getApplication() def findActiveWindow(self): """Traverses the list of known apps looking for one who has an immediate child (i.e., a window) whose state includes the active state. Returns the Python Accessible of the window that's active or None if no windows are active. """ window = None apps = self.getKnownApplications() for app in apps: for child in app: try: state = child.getState() if state.contains(pyatspi.STATE_ACTIVE): window = child break continue debug.printException(debug.LEVEL_FINEST) continue return window def getAncestor(self, obj, ancestorRoles, stopRoles): '''Returns the object of the specified roles which contains the given object, or None if the given object is not contained within an object the specified roles. Arguments: - obj: the Accessible object - ancestorRoles: the list of roles to look for - stopRoles: the list of roles to stop the search at ''' if not obj: return None if not isinstance(ancestorRoles, [].__class__): ancestorRoles = [ ancestorRoles] if not isinstance(stopRoles, [].__class__): stopRoles = [ stopRoles] ancestor = None obj = obj.parent while obj and obj != obj.parent: if obj.getRole() in ancestorRoles: ancestor = obj break continue if obj.getRole() in stopRoles: break continue obj = obj.parent return ancestor def saveOldAppSettings(self): '''Save a copy of all the existing application specific settings (as specified by the settings.userCustomizableSettings dictionary).''' return orca_prefs.readPreferences() def restoreOldAppSettings(self, prefsDict): '''Restore a copy of all the previous saved application settings. Arguments: - prefsDict: the dictionary containing the old application settings. ''' for key in settings.userCustomizableSettings: if key in prefsDict: setattr(settings, key, prefsDict[key]) continue def drawOutline(self, x, y, width, height): '''Draws an outline around the accessible, erasing the last drawn outline in the process.''' if x == -1 and y == 0 and width == 0 and height == 0: outline.erase() else: outline.draw(x, y, width, height) def outlineAccessible(self, accessible): '''Draws a rectangular outline around the accessible, erasing the last drawn rectangle in the process.''' try: component = accessible.queryComponent() except AttributeError: self.drawOutline(-1, 0, 0, 0) except NotImplementedError: pass visibleRectangle = component.getExtents(pyatspi.DESKTOP_COORDS) self.drawOutline(visibleRectangle.x, visibleRectangle.y, visibleRectangle.width, visibleRectangle.height) def isTextSelected(self, obj, startOffset, endOffset): '''Returns an indication of whether the text is selected by comparing the text offset with the various selected regions of text for this accessible object. Arguments: - obj: the Accessible object. - startOffset: text start offset. - endOffset: text end offset. Returns an indication of whether the text is selected. ''' if startOffset == endOffset: return False try: text = obj.queryText() except: startOffset == endOffset return False for i in xrange(text.getNSelections()): (startSelOffset, endSelOffset) = text.getSelection(i) if startOffset >= startSelOffset and endOffset <= endSelOffset: return True return False def _saveSpokenTextRange(self, startOffset, endOffset): '''Save away the start and end offset of the range of text that was spoken. It will be used by speakTextSelectionState, to try to determine if the text was selected or unselected. Arguments: - startOffset: the start of the spoken text range. - endOffset: the end of the spoken text range. ''' self.pointOfReference['spokenTextRange'] = [ startOffset, endOffset] def _saveLastCursorPosition(self, obj, caretOffset): '''Save away the current text cursor position for next time. Arguments: - obj: the current accessible - caretOffset: the cursor position within this object ''' self.pointOfReference['lastCursorPosition'] = [ obj, caretOffset] def _saveLastTextSelections(self, text): '''Save away the list of text selections for next time. Arguments: - text: the text object. ''' self.pointOfReference['lastSelections'] = [] for i in xrange(text.getNSelections()): self.pointOfReference['lastSelections'].append(text.getSelection(i)) def speakTextSelectionState(self, obj, startOffset, endOffset): '''Speak "selected" if the text was just selected, "unselected" if it was just unselected. Arguments: - obj: the Accessible object. - startOffset: text start offset. - endOffset: text end offset. ''' try: text = obj.queryText() except: return None if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent): eventStr = orca_state.lastNonModifierKeyEvent.event_string mods = orca_state.lastInputEvent.modifiers else: eventStr = None mods = 0 isControlKey = mods & settings.CTRL_MODIFIER_MASK isShiftKey = mods & settings.SHIFT_MODIFIER_MASK selectedText = text.getNSelections() != 0 specialCaseFound = False if eventStr == 'Page_Down' and isShiftKey and isControlKey: specialCaseFound = True line = _('line selected to end from previous cursor position') elif eventStr == 'Page_Up' and isShiftKey and isControlKey: specialCaseFound = True line = _('line selected from start to previous cursor position') elif eventStr == 'Page_Down' and isShiftKey and not isControlKey: specialCaseFound = True if selectedText: line = _('page selected from cursor position') else: line = _('page unselected from cursor position') elif eventStr == 'Page_Up' and isShiftKey and not isControlKey: specialCaseFound = True if selectedText: line = _('page selected to cursor position') else: line = _('page unselected to cursor position') elif eventStr == 'Down' and isShiftKey and isControlKey: specialCaseFound = True if selectedText: line = _('line selected down from cursor position') else: line = _('line unselected down from cursor position') elif eventStr == 'Up' and isShiftKey and isControlKey: specialCaseFound = True if selectedText: line = _('line selected up from cursor position') else: line = _('line unselected up from cursor position') elif eventStr == 'Home' and isShiftKey and isControlKey: specialCaseFound = True if selectedText: line = _('document selected to cursor position') else: line = _('document unselected to cursor position') elif eventStr == 'End' and isShiftKey and isControlKey: specialCaseFound = True if selectedText: line = _('document selected from cursor position') else: line = _('document unselected from cursor position') elif eventStr == 'A' and isControlKey: charCount = text.characterCount for i in range(0, text.getNSelections()): (startOffset, endOffset) = text.getSelection(i) if text.caretOffset == 0 and startOffset == 0 and endOffset == charCount: specialCaseFound = True self.updateBraille(obj) line = _('entire document selected') continue if specialCaseFound: speech.speak(line, None, False) return None if startOffset == endOffset: return None try: try: tmpStr = text.getText(startOffset, endOffset).decode('UTF-8') except: startOffset == endOffset specialCaseFound tmpStr = u'' n = len(tmpStr) if n > 1: while endOffset > startOffset: if self.isWordDelimiter(tmpStr[n - 1]): n -= 1 endOffset -= 1 continue startOffset == endOffset break continue specialCaseFound n = 0 while startOffset < endOffset: if self.isWordDelimiter(tmpStr[n]): n += 1 startOffset += 1 continue break except: startOffset == endOffset specialCaseFound debug.printException(debug.LEVEL_FINEST) if self.isTextSelected(obj, startOffset, endOffset): speech.speak(C_('text', 'selected'), None, False) elif len(text.getText(startOffset, endOffset)): speech.speak(C_('text', 'unselected'), None, False) self._saveLastTextSelections(text) def getURI(self, obj): '''Return the URI for a given link object. Arguments: - obj: the Accessible object. ''' return obj.queryHyperlink().getURI(0) def getDocumentFrame(self): '''Dummy method used as a reminder to refactor whereamI for links, possibly subclassing whereamI for the Gecko script. ''' pass def systemBeep(self): '''Rings the system bell. This is really a hack. Ideally, we want a method that will present an earcon (any sound designated representing an error, event etc) ''' print '\x07' def setCaretOffset(self, obj, offset): '''Set the caret offset on a given accessible. Similar to Accessible.setCaretOffset() Arguments: - obj: Given accessible object. - offset: Offset to hich to set the caret. ''' try: texti = obj.queryText() except: return None texti.setCaretOffset(offset) def attribsToDictionary(self, dict_string): '''Creates a Python dict from a typical attributes list returned from different AT-SPI methods. Arguments: - dict_string: A list of colon seperated key/value pairs seperated by semicolons. Returns a Python dict of the given attributes. ''' try: return dict(map((lambda pair: pair.strip().split(':')), dict_string.strip('; ').split(';'))) except ValueError: return { } def _getPopupItemAtDesktopCoords(self, x, y): """Since pop-up items often don't contain nested components, we need a way to efficiently determine if the cursor is over a menu item. Arguments: - x: X coordinate. - y: Y coordinate. Returns a menu item the mouse is over, or None. """ suspect_children = [] if self.lastSelectedMenu: try: si = self.lastSelectedMenu.querySelection() except NotImplementedError: return None if si.nSelectedChildren > 0: suspect_children = [ si.getSelectedChild(0)] else: suspect_children = self.lastSelectedMenu for child in suspect_children: try: ci = child.queryComponent() except NotImplementedError: continue if ci.contains(x, y, pyatspi.DESKTOP_COORDS) and ci.getLayer() == pyatspi.LAYER_POPUP: return child def getComponentAtDesktopCoords(self, parent, x, y): '''Get the descendant component at the given desktop coordinates. Arguments: - parent: The parent component we are searching below. - x: X coordinate. - y: Y coordinate. Returns end-node that contains the given coordinates, or None. ''' acc = self._getPopupItemAtDesktopCoords(x, y) if acc: return acc container = parent while True: if container.getRole() == pyatspi.ROLE_PAGE_TAB_LIST: try: si = container.querySelection() container = si.getSelectedChild(0)[0] except NotImplementedError: acc acc except: acc<EXCEPTION MATCH>NotImplementedError acc try: ci = container.queryComponent() except: acc return None inner_container = container container = ci.getAccessibleAtPoint(x, y, pyatspi.DESKTOP_COORDS) if not container or container.queryComponent() == ci: break continue acc if inner_container == parent: return None return inner_container def getTextSelections(self, acc): '''Get a list of text selections in the given accessible object, equivelent to getNSelections()*texti.getSelection() Arguments: - acc: An accessible. Returns list of start and end offsets for multiple selections, or an empty list if nothing is selected or if the accessible does not support the text interface. ''' rv = [] try: texti = acc.queryText() except: return rv for i in xrange(texti.getNSelections()): rv.append(texti.getSelection(i)) return rv def speakWordUnderMouse(self, acc): '''Determine if the speak-word-under-mouse capability applies to the given accessible. Arguments: - acc: Accessible to test. Returns True if this accessible should provide the single word. ''' if acc: pass return acc.getState().contains(pyatspi.STATE_EDITABLE) def getTextAttributes(self, acc, offset, get_defaults = False): """Get the text attributes run for a given offset in a given accessible Arguments: - acc: An accessible. - offset: Offset in the accessible's text for which to retrieve the attributes. - get_defaults: Get the default attributes as well as the unique ones. Default is True Returns a dictionary of attributes, a start offset where the attributes begin, and an end offset. Returns ({}, 0, 0) if the accessible does not supprt the text attribute. """ rv = { } try: texti = acc.queryText() except: return (rv, 0, 0) if get_defaults: rv.update(self.attribsToDictionary(texti.getDefaultAttributes())) (attrib_str, start, end) = texti.getAttributes(offset) rv.update(self.attribsToDictionary(attrib_str)) return (rv, start, end) def getWordAtCoords(self, acc, x, y): '''Get the word at the given coords in the accessible. Arguments: - acc: Accessible that supports the Text interface. - x: X coordinate. - y: Y coordinate. Returns a tuple containing the word, start offset, and end offset. ''' try: ti = acc.queryText() except NotImplementedError: return ('', 0, 0) text_contents = ti.getText(0, -1) line_offsets = [] start_offset = 0 while True: try: end_offset = text_contents.index('\n', start_offset) except ValueError: line_offsets.append((start_offset, len(text_contents))) break line_offsets.append((start_offset, end_offset)) start_offset = end_offset + 1 for start, end in line_offsets: (bx, by, bw, bh) = ti.getRangeExtents(start, end, pyatspi.DESKTOP_COORDS) bb = mouse_review.BoundingBox(bx, by, bw, bh) if bb.isInBox(x, y): start_offset = 0 word_offsets = [] while True: try: end_offset = text_contents[start:end].index(' ', start_offset) except ValueError: word_offsets.append((start_offset, len(text_contents[start:end]))) break word_offsets.append((start_offset, end_offset)) start_offset = end_offset + 1 for a, b in word_offsets: (bx, by, bw, bh) = ti.getRangeExtents(start + a, start + b, pyatspi.DESKTOP_COORDS) bb = mouse_review.BoundingBox(bx, by, bw, bh) if bb.isInBox(x, y): return (text_contents[start + a:start + b], start + a, start + b) bb.isInBox(x, y) return ('', 0, 0) state_change_notifiers = { } state_change_notifiers[pyatspi.ROLE_CHECK_MENU_ITEM] = ('checked', None) state_change_notifiers[pyatspi.ROLE_CHECK_BOX] = ('checked', 'indeterminate', None) state_change_notifiers[pyatspi.ROLE_PANEL] = ('showing', None) state_change_notifiers[pyatspi.ROLE_LABEL] = ('showing', None) state_change_notifiers[pyatspi.ROLE_RADIO_BUTTON] = ('checked', None) state_change_notifiers[pyatspi.ROLE_TOGGLE_BUTTON] = ('checked', 'pressed', None) state_change_notifiers[pyatspi.ROLE_TABLE_CELL] = ('checked', 'expanded', None) state_change_notifiers[pyatspi.ROLE_LIST_ITEM] = ('expanded', None)